[csw-devel] SF.net SVN: gar:[13271] csw/mgar/gar/v2
wbonnet at users.sourceforge.net
wbonnet at users.sourceforge.net
Sun Feb 13 14:14:18 CET 2011
Revision: 13271
http://gar.svn.sourceforge.net/gar/?rev=13271&view=rev
Author: wbonnet
Date: 2011-02-13 13:14:18 +0000 (Sun, 13 Feb 2011)
Log Message:
-----------
MErging uwatch2 branch
Modified Paths:
--------------
csw/mgar/gar/v2/categories/cpan/category.mk
Added Paths:
-----------
csw/mgar/gar/v2/bin/uwatch
Removed Paths:
-------------
csw/mgar/gar/v2/bin/upstream_watch
Deleted: csw/mgar/gar/v2/bin/upstream_watch
===================================================================
--- csw/mgar/gar/v2/bin/upstream_watch 2011-02-13 12:55:58 UTC (rev 13270)
+++ csw/mgar/gar/v2/bin/upstream_watch 2011-02-13 13:14:18 UTC (rev 13271)
@@ -1,208 +0,0 @@
-#!/usr/bin/perl -lw
-#
-# Copyright 2006 Yann Rouillard <yann at blastwave.org>
-# All rights reserved. Use is subject to license terms.
-#
-# Redistribution and/or use, with or without modification, is
-# permitted. This software is without warranty of any kind. The
-# author(s) shall not be liable in the event that use of the
-# software causes damage.
-#
-# upstream_watch - search for new upstream files
-#
-
-use Env;
-use Getopt::Long;
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-# Function : compare_versions
-# Purpose : Compare version number of two software
-# Arguments: $version1 - version number of the first software
-# $version2 - version number of the first software
-# Returns : 1 if $version1 > $version2
-# 0 if $version1 == $version2
-# -1 if $version1 < $verson2
-#
-sub compare_versions
-{
- my $version1 = shift;
- my $version2 = shift;
-
- # we consider the version to be composed of several elements separated by '.' ',' or '_'
- # an elements can be a string or a number
- # at each step we extract the next elements of the two version strings and compare them
- while (my ($number1, $string1, $rem1) = ($version1 =~ /^(?:([0-9]+)|([^0-9\.,_]+))[\.,_]?(.*)?$/)) {
-
- my ($number2, $string2, $rem2) = ($version2 =~ /^(?:([0-9]+)|([^0-9\.,_]+))[\.,_]?(.*)?$/) or
- # $versions1 if the same as $versions with additional characters so it must be more recent
- # (i.e. 1.2foo is usually more recent than 1.2)
- return 1;
-
- if (defined $number1 and defined $number2) {
- my $ret = ($number1 <=> $number2);
- return $ret if $ret != 0;
- } elsif (defined $string1 and defined $string2) {
- # string comparisons is relevevant for comparing
- # version strings like 2.a and 2.b
- my $ret = ($string1 cmp $string2);
- return $ret if $ret != 0;
- } elsif (defined $number1 and defined $string2) {
- # we suppose that numbers are always greater that string
- return 1;
- } else {
- return -1;
- }
-
- $version1 = $rem1;
- $version2 = $rem2;
- }
-
- if ($version2 ne "") {
- # $version2 if the same as $version1 with additionnal characters so it must be more recent
- # (i.e. 1.2foo is usually more recent than 1.2)
- return -1;
- }
-
- return 0;
-}
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-# Function : get_file_list
-# Purpose : Return the list of files available at a given url
-# Arguments: $url - an url
-# Returns : the list of files availables at the given url
-#
-# TODO: find a better way than lftp to do this
-#
-sub get_file_list_lftp
-{
- my $url = shift;
- my @file_list;
-
- my $ftp_proxy_cmd = "";
- my $http_proxy_cmd = "";
-
- # lftp doesn't seem to obey the _PROXY env variable
- # we must manually set them
- $ftp_proxy_cmd = "set ftp:proxy $ENV{FTP_PROXY};" if exists $ENV{FTP_PROXY};
- $ftp_proxy_cmd = "set ftp:proxy $ENV{ftp_proxy};" if exists $ENV{ftp_proxy};
- $http_proxy_cmd = "set http:proxy $ENV{HTTP_PROXY};" if exists $ENV{HTTP_PROXY};
- $http_proxy_cmd = "set http:proxy $ENV{http_proxy};" if exists $ENV{http_proxy};
- $https_proxy_cmd = "set https:proxy $ENV{HTTPS_PROXY};" if exists $ENV{HTTPS_PROXY};
- $https_proxy_cmd = "set https:proxy $ENV{https_proxy};" if exists $ENV{https_proxy};
-
- open (FH, "lftp -q -c \"set net:timeout 30; set net:max-retries 16; $ftp_proxy_cmd $http_proxy_cmd $https_proxy_cmd open $url/ && ls\" 2>/dev/null |");
-
- while (my $line = <FH>) {
- my @cols = split (/\s+/, $line);
- my $filename = $cols[$#cols];
- chomp ($filename);
- my $result = rindex($filename, '/');
- if ($result != -1) {
- $filename = substr $filename , $result +1 ;
- }
- push (@file_list, $filename);
- }
-
- close (FH);
-
- return \@file_list;
-}
-
-sub get_file_list_wget_sourceforge
-{
- my $url = shift;
- my @file_list;
-
- my $http_proxy_cmd = "";
- my $wget_command ="wget";
- my $filename = "";
-
- # lftp doesn't seem to obey the _PROXY env variable
- # we must manually set them
- $http_proxy_cmd = "set http:proxy $ENV{HTTP_PROXY};" if exists $ENV{HTTP_PROXY};
- $http_proxy_cmd = "set http:proxy $ENV{http_proxy};" if exists $ENV{http_proxy};
- $wget_command = "$http_proxy_cmd ; wget --proxy=on" if exists $ENV{http_proxy_cmd};
-
- open (FH, "$wget_command -qO- $url 2>/dev/null | grep class | grep selected | grep li | grep Download | ");
-
- if (my $line = <FH>) {
- my @cols = split (/"/, $line);
- $filename = $cols[3];
- chomp ($filename);
- }
- else {
- close (FH);
- return \@file_list;
- }
-
- close (FH);
-
- $url = "http://downloads.sourceforge.net" . $filename;
- open (FH, "$wget_command -qO- $url 2>/dev/null | grep $filename | grep package_id= | grep release_id | ");
-
- while (my $line = <FH>) {
- my @cols = split (/>/, $line);
- my $filename = $cols[2];
- chomp ($filename);
- @cols = split (/</, $filename);
- $filename = $cols[0];
- chomp ($filename);
- push (@file_list, $filename);
- }
-
- close (FH);
-
- return \@file_list;
-}
-
-my $help;
-my @urls;
-my $use_sf = 0;
-
-Getopt::Long::Configure ("no_ignore_case");
-GetOptions("h|help" => \$help,
- "s|use_sf=i" => \$use_sf,
- "u|url=s@" => \@urls);
-
-if ($help) {
- print <<EOF
-Usage: upstream_watch -u URL file_regex...
-Display the latest upstream files.
-EOF
-}
-
-foreach my $file_pattern (@ARGV) {
-
- my $newest_version = 0;
- my $newest_file = "";
- my $file_list ;
-
- foreach my $url (@urls) {
- if ($use_sf != 0) {
- $file_list = get_file_list_wget_sourceforge ($url);
- } else {
- $file_list = get_file_list_lftp ($url);
- }
-
- foreach my $file (@{$file_list}) {
- if ($file =~ /^$file_pattern$/) {
- my $char = '/';
- my $result = rindex($file, $char);
- if ($result != -1) {
- $file = substr $file , $result +1 ;
- }
-
- # we only keep the newest file for a given $file_pattern
- if (compare_versions ($1, $newest_version) > 0) {
- $newest_version = $1;
- $newest_file = $file;
- }
- }
- }
- }
- printf "$newest_file " if $newest_version;
-}
-printf "\n";
-
Copied: csw/mgar/gar/v2/bin/uwatch (from rev 13270, csw/mgar/gar/v2-uwatch2/bin/uwatch)
===================================================================
--- csw/mgar/gar/v2/bin/uwatch (rev 0)
+++ csw/mgar/gar/v2/bin/uwatch 2011-02-13 13:14:18 UTC (rev 13271)
@@ -0,0 +1,1676 @@
+#!/opt/csw/bin/python2.6
+
+# TODO : check arguments for valid date
+# TODO : check arguments for emtpy strings
+
+#
+# The contents of this file are subject to the COMMON DEVELOPMENT AND
+# DISTRIBUTION LICENSE (CDDL) (the "License"); you may not use this
+# file except in compliance with the License.
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 3 or later (the "GPL"),
+# in which case the provisions of the GPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL, and not to allow others to
+# use your version of this file under the terms of the CDDL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the CDDL, or the GPL.
+#
+# Copyright 2010 OpenCSW (http://www.opencsw.org). All rights reserved.
+# Use is subject to license terms.
+#
+#
+# Contributors list :
+#
+# William Bonnet wbonnet at opencsw.org
+#
+#
+
+# Import all the needed modules
+import sys
+import string
+import re
+import os
+import shutil
+import subprocess
+import pysvn
+import MySQLdb
+import datetime
+import ConfigParser
+
+from urllib2 import Request, urlopen, URLError
+from optparse import OptionParser
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class InvalidSourceDirectoryContentException(Exception):
+ """Exception raised when a method is called on the Abstract command class
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, message):
+ self.message = message
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class AbstractCommandMethodCallException(Exception):
+ """Exception raised when a method is called on the Abstract command 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 DatabaseConnectionException(Exception):
+ """Exception raised when an error occur while connecting to the database
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, message):
+ self.message = message
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class DuplicatePackageException(Exception):
+ """Exception raised when an error occur while connecting to the database
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, message):
+ self.message = message
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class UpstreamUrlRetrievalFailedException(Exception):
+ """Exception raised when an unsuported protocol is specified in the upstream url
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, url):
+ self.url = url
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class NoUpstreamVersionFoundException(Exception):
+ """Exception raised when searching the upstream page content does not match the regexp
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, url, regexp):
+ self.url = url
+ self.regexp = regexp
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class MissingArgumentException(Exception):
+ """Exception raised when a command line argument is missing
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, value):
+ self.parameter = value
+
+ def __str__(self):
+ return repr(self.parameter)
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class InvalidArgumentException(Exception):
+ """Exception raised when a command line argument is invalid. For instance an invalid version or date
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, value):
+ self.parameter = value
+
+ def __str__(self):
+ return repr(self.parameter)
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class CommandLineParser(object):
+ """This class is used to parse command line. It process only options. Command verb is parsed by the main procedure
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self):
+ # Create the parser object
+ self.optionParser = OptionParser()
+
+ # Add options to parser
+ self.optionParser.add_option("--verbose", help="Activate verbose mode", action="store_true", dest="verbose")
+ self.optionParser.add_option("--current-version", help="Current package version", action="store", dest="current_version")
+ self.optionParser.add_option("--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.optionParser.add_option("--regexp", help="Version matching regular expression", action="store", dest="regexp")
+ self.optionParser.add_option("--source-directory", help="Source directory (place from where the build is copied). Default value is current directory", action="store", dest="source_directory")
+ self.optionParser.add_option("--target-version", help="Package target version", action="store", dest="target_version")
+ self.optionParser.add_option("--upstream-url", help="Upstream version page url", action="store", dest="upstream_url")
+
+ # Option used for reporting uwatch result
+ self.optionParser.add_option("--uwatch-error", help="Flag used to report a uwatch error", action="store_true", dest="uwatch_error")
+ self.optionParser.add_option("--uwatch-output", help="Flag used to report the uwatch output", action="store", dest="uwatch_output")
+ self.optionParser.add_option("--uwatch-deactivated",help="Flag used to report a uwatch error", action="store_true", dest="uwatch_deactivated")
+ self.optionParser.add_option("--uwatch-pkg-root", help="Defines the uwatch package root working directory", action="store", dest="uwatch_pkg_root")
+
+ # Option used for storing version information in the database
+ self.optionParser.add_option("--gar-distfiles", help="Gar version of the package", action="store", dest="gar_distfiles")
+ self.optionParser.add_option("--gar-version", help="Gar version of the package", action="store", dest="gar_version")
+ self.optionParser.add_option("--upstream-version", help="Upstream version of the package", action="store", dest="upstream_version")
+ self.optionParser.add_option("--gar-path", help="Relative path in svn repository", action="store", dest="gar_path")
+ self.optionParser.add_option("--catalog-name", help="Catalog name", action="store", dest="catalog_name")
+ self.optionParser.add_option("--package-name", help="Package name", action="store", dest="package_name")
+ self.optionParser.add_option("--execution-date", help="Check date to be stored in the database", action="store", dest="execution_date")
+
+ self.optionParser.add_option("--database-schema", help="Defines the database to use in the connection string", action="store", dest="database_schema")
+ self.optionParser.add_option("--database-host", help="Defines the database host to use in the connection string", action="store", dest="database_host")
+ self.optionParser.add_option("--database-user", help="Defines the database user to use in the connection string", action="store", dest="database_user")
+ self.optionParser.add_option("--database-password", help="Defines the database password to use in the connection string", action="store", dest="database_password")
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def parse(self):
+ (self.options, self.args) = self.optionParser.parse_args()
+ return self.options, self.args
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class UwatchConfiguration(object):
+ """This handles parameters retrieved either from command line or configuration file.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def initialize(self, args):
+ """Initialize configuration object. If configurations contains files reference, values from the files are
+ read and copied into this object. Next attempts to retrieve files stored information will read values from
+ this object.
+ """
+
+ # Default is no file parser
+ fileParser = None
+
+ # Test if the .uwatchrc file exists
+ if os.path.isfile( os.path.expanduser("~/.uwatchrc") ):
+ # Yes thus load values from the configuration file before processing args parameter
+ # This allow to override from the command line some values stored in the config file
+ fileParser = ConfigParser.ConfigParser()
+ fileParser.read( [os.path.expanduser("~/.uwatchrc") ] )
+
+ # Read the database schema from the config file
+ try:
+ self._database_schema = fileParser.get("main", "database-schema")
+ except Config.NoOptionError:
+ self._database_schema = None
+
+ # Read the database hostname from the config file
+ try:
+ self._database_host = fileParser.get("main", "database-host")
+ except Config.NoOptionError:
+ self._database_host = None
+
+ # Read the database user from the config file
+ try:
+ self._database_user = fileParser.get("main", "database-user")
+ except Config.NoOptionError:
+ self._database_user = None
+
+ # Read the database password from the config file
+ try:
+ self._database_password = fileParser.get("main", "database-password")
+ except Config.NoOptionError:
+ self._database_password = None
+
+ # Read the package root working dir from the config file
+ try:
+ self._uwatch_pkg_root = fileParser.get("main", "uwatch-pkg-root")
+ except Config.NoOptionError:
+ self._uwatch_pkg_root = None
+
+ # This member variable is a flag which defines the status of the verbose mode (True : activated)
+ if args.verbose != None:
+ self._verbose = args.verbose
+ else:
+ self._verbose = False
+
+ # This member variable defines the value of the version of the package
+ # Current revision is not passed as a separated argument. It is part of the opencsw version number.
+ # Package version are defined as follow : version[,REV=revision]*
+ if args.current_version != None:
+
+ # Parse the version string
+ ver = re.split(r"(?P<version>.*),REV=(?P<revision>.*)", args.current_version)
+
+ # Test if this is a match
+ if ver == None:
+ # No, thus raise an exception
+ msg = "Unable to parse %(version)s as a valid package version" % { 'version' : args.current_version }
+ raise InvalidArgumentException(msg)
+ else:
+ # If the length of array is one, the no revision is provided
+ if len(ver) == 1:
+ self._current_version = ver[0]
+ self._current_revision = ""
+ else:
+ self._current_version = ver[1]
+ self._current_revision = ver[2]
+
+ # Attempt to split again the string. If len is greater than 1
+ # Then there are at least two rev string => raise exception
+ ver = re.split(r"(?P<version>.*),REV=(?P<revision>.*)", self._current_version)
+ if len(ver) > 1:
+ msg = "Unable to parse %(version)s as a valid package version. There are more than one revision string" % { 'version' : args.current_version }
+ raise InvalidArgumentException(msg)
+
+ # This member variable defines the value of the regexp used to match the upstream web page
+ if args.regexp != None:
+ self._regexp = args.regexp
+
+ # This member variable defines the url of the upstream web page used to check for new version
+ 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
+
+ # This member variable defines the version of the package stored in the gar build description
+ if args.gar_version != None:
+ self._gar_version = args.gar_version
+
+ # This member variable defines the upstream version of package
+ if args.upstream_version != None:
+ self._upstream_version = args.upstream_version
+
+ # This member variable defines the relative path in the svn repository
+ if args.gar_path != None:
+ self._gar_path = args.gar_path
+
+ # This member variable defines the catalog name of the package
+ if args.catalog_name != None:
+ self._catalog_name = args.catalog_name
+
+ # This member variable defines the package name
+ if args.package_name != None:
+ self._package_name = args.package_name
+
+ # This member variable defines the date of the execution (it is useful to be able to define a date to construct history of versions)
+ if args.execution_date != None:
+ self._execution_date = args.execution_date
+
+ # This member variable defines the database to use in the connection string
+ if args.database_schema != None:
+ self._database_schema = args.database_schema
+
+ # This member variable defines the database host to use in the connection string
+ if args.database_host != None:
+ self._database_host = args.database_host
+
+ # This member variable defines the database user to use in the connection string
+ if args.database_user != None:
+ self._database_user = args.database_user
+
+ # This member variable defines the database password to use in the connection string
+ if args.database_password != None:
+ self._database_password = args.database_password
+
+ # This member variable defines the uwatch activation status
+ if args.uwatch_deactivated != None:
+ self._uwatch_deactivated = True
+ else:
+ self._uwatch_deactivated = False
+
+ # This member variable defines the uwatch last report result
+ if args.uwatch_error != None:
+ self._uwatch_error = True
+ else:
+ self._uwatch_error = False
+
+ # This member variable defines the uwatch last report result
+ if args.uwatch_pkg_root != None:
+ self._uwatch_pkg_root = args.uwatch_pkg_root
+
+ # This member variable defines the uwatch last report output
+ if args.uwatch_output != None:
+ self._uwatch_output = args.uwatch_output
+
+ # This member variable defines the gar distfiles
+ if args.gar_distfiles != None:
+ self._gar_distfiles = args.gar_distfiles
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self):
+
+ # Initialize all variables to None. Even if useless, the purpose of this to keep up to date the list
+ self._verbose = None
+ self._uwatch_deactivated = False
+ self._uwatch_error = False
+ self._uwatch_pkg_root = None
+ self._current_version = None
+ self._current_revision = ""
+ self._regexp = None
+ self._upstream_url = None
+ self._target_version = None
+ self._source_directory = "."
+ self._target_location = "../branches"
+ self._gar_version = None
+ self._upstream_version = None
+ self._gar_path = None
+ self._catalog_name = None
+ self._package_name = None
+ self._execution_date = None
+ self._database_schema = None
+ self._database_host = None
+ self._database_user = None
+ self._database_password = None
+ self._gar_distfiles = None
+ self._uwatch_output = None
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getCurrentVersion(self):
+ return self._current_version
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getCurrentRevision(self):
+ return self._current_revision
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getRegexp(self):
+ # In case the regexp is not define, we try to guess it using the distfile
+ if (self._regexp == None) & (self._gar_distfiles != None):
+ # Instanciate a regexp generator
+ urg = UwatchRegexGenerator()
+
+ # Retrieve the regexp list
+ auto_regex_list = urg.GenerateRegex(self._catalog_name, self._gar_distfiles)
+
+ # Use the first item as current regexp
+ self._regexp = auto_regex_list[0]
+
+ return self._regexp
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUpstreamURL(self):
+ return self._upstream_url
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ 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
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getGarVersion(self):
+ return self._gar_version
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUpstreamVersion(self):
+ return self._upstream_version
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getGarPath(self):
+ if self.getUwatchPkgRoot():
+ return self._gar_path.replace(self.getUwatchPkgRoot(), "")
+ else:
+ return self._gar_path
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getCatalogName(self):
+ return self._catalog_name
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getPackageName(self):
+ return self._package_name
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getExecutionDate(self):
+ return self._execution_date
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getDatabaseSchema(self):
+ return self._database_schema
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getDatabaseHost(self):
+ return self._database_host
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getDatabaseUser(self):
+ return self._database_user
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getDatabasePassword(self):
+ return self._database_password
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUwatchError(self):
+ return self._uwatch_error
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUwatchDeactivated(self):
+ return self._uwatch_deactivated
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUwatchPkgRoot(self):
+ return self._uwatch_pkg_root
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getGarDistfiles(self):
+ return self._gar_distfiles
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getUwatchOutput(self):
+ return self._uwatch_output
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class AbstractCommand(object):
+ """Base class for command implementation. You should create a derived class per command to implemente.
+ A "command" represent a concrete action verb given to the program.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ self.config = UwatchConfiguration()
+ self.name = name
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def getName(self):
+ return self.name
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def execute(self, opts, arguments):
+ print "Internal error : Abstract command method called\n"
+ raise AbstractCommandMethodCallException("execute")
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class UpstreamWatchCommand(AbstractCommand):
+ """UpstreamWatch command. This command is the base class used for all upstream watch commands. It provides argument checking
+ url content retrieving, version comparison, etc.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(UpstreamWatchCommand, self).__init__(name)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def UrlContentRetrieve(self, url):
+
+ try:
+ # Request the upstream URL and open it
+ req = Request(url)
+ response = urlopen(req)
+
+ except URLError, e:
+ if hasattr(e, 'reason'):
+ print 'We failed to reach a server during retrieval of : ' + url
+ print 'Reason: ', e.reason
+ elif hasattr(e, 'code'):
+ print 'The server couldn\'t fulfill the request during retrieval of : ' + url
+ print 'Error code: ', e.code
+
+ # Check for response code value. We should get a 200
+ raise UpstreamUrlRetrievalFailedException(url)
+
+ else:
+ # everything is fine, retrieve the page content
+ return response.read()
+
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def CompareVersionAndGetNewest(self, version1, version2):
+
+ # we consider the version to be composed of several elements separated by '.' ',' '-' or '_'
+ # an elements can be a string or a number
+ # at each step we extract the next elements of the two version strings and compare them
+
+ if isinstance(version1, str) == False:
+ print "Version is not a string. Please check environnement variable UFILES_REGEX"
+ print version1
+
+ if isinstance(version2, str) == False:
+ print "Version is not a string. Please check environnement variable UFILES_REGEX"
+ print version2
+
+ # Retrieve the tokens from both version. Use . - , and _ as splitters
+ splittingRegExp = "(?:[\.,_-])"
+ tokens1 = re.split(splittingRegExp, version1)
+ tokens2 = re.split(splittingRegExp, version2)
+
+ # Iterates the toeksn of version 1, pop tokens one by one and compare to the token at same
+ # in version 2
+ while len(tokens1) > 0:
+ # If we still have tokens in version 1 and version 2 is empty, then version 1 is newer
+ # TODO: may have to deal with beta suffixes...
+ if len(tokens2) == 0:
+ return version1
+
+ # Convert both elements to integer
+ # TODO : handles chars in versions
+ elem1 = tokens1.pop(0)
+ elem2 = tokens2.pop(0)
+
+ # If both elements are integer, then convert to int before comparison, otherwise compare strings
+ try:
+ elem1 = int(elem1)
+ elem2 = int(elem2)
+ except:
+ elem1 = str(elem1)
+ elem2 = str(elem2)
+ # print "Doing string comparison"
+
+ # print "Comparing %(elem1)s and %(elem2)s" % { 'elem1' : elem1 , 'elem2' : elem2 }
+
+ # if elements are the same continue the loop
+ if elem1 == elem2:
+ continue
+
+ # Test if elem1 is more recent
+ if elem1 > elem2:
+ # print "%(elem1)s > %(elem2)s" % { 'elem1' : elem1 , 'elem2' : elem2 }
+ return version1
+ else:
+ # print "%(elem1)s < %(elem2)s" % { 'elem1' : elem1 , 'elem2' : elem2 }
+ return version2
+
+ return version1
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class CheckUpstreamCommand(UpstreamWatchCommand):
+ """CheckUpstream command. This command retrieve the upstream web page and search for a new version. Version check is
+ done by matching the regexp from the makefile on the page. Results are sorted to get the highest available and
+ compared to current version
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(CheckUpstreamCommand, self).__init__(name)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkArgument(self):
+
+ # Variable used to flag that we have a missing argument
+ argsValid = True
+
+ # Current version is mandatory
+ if self.config.getCurrentVersion() == None:
+ print "Error : Current version is not defined. Please use --current-version flag, or --help to display help"
+ argsValid = False
+
+ # Regexp is mandatory
+ if self.config.getRegexp() == None:
+ print "Error : Regexp is not defined. Please use --regexp flag, or --help to display help"
+ argsValid = False
+
+ # UpstreamURL is mandatory
+ if self.config.getUpstreamURL() == None:
+ print "Error : Upstream version page URL is not defined. Please use --upstream-url 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 execute(self, opts, args):
+
+ try:
+
+ # Initialize configuration
+ self.config.initialize(opts)
+
+ # Need a way to check that all options needed are available
+ self.checkArgument()
+
+ # Call the method in charge of retrieving upstream content
+ content = self.UrlContentRetrieve(self.config.getUpstreamURL())
+
+ # Search the strings matching the regexp passed through command line arguments
+ p = re.compile(self.config.getRegexp())
+ matches = p.findall(content)
+
+ # Check if we have found some results
+ if len(matches) == 0:
+ raise NoUpstreamVersionFoundException(self.config.getUpstreamURL(), self.config.getRegexp())
+ else:
+ newestVersion = self.config.getCurrentVersion()
+ while len(matches) > 0:
+ newestVersion = self.CompareVersionAndGetNewest(newestVersion, matches.pop(0))
+
+ # At the end of the processing loop, test if we have a newer version avail, if yes output it
+ if newestVersion <> self.config.getCurrentVersion():
+ print newestVersion
+
+ # Exit after processing, eveythin gis ok, return true to the command processor
+ return True
+
+ except MissingArgumentException, (instance):
+
+ # Display a cool error message :)
+ print instance.parameter
+
+ # 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
+ return False
+
+ except NoUpstreamVersionFoundException, (instance):
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class GetUpstreamLatestVersionCommand(UpstreamWatchCommand):
+ """GetUpstreamLatestVersion command. This command retrieve the upstream web page and search for the latest version.
+ Version check is done by matching the regexp from the makefile on the page. Results are sorted to get the newest version
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(GetUpstreamLatestVersionCommand, self).__init__(name)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkArgument(self):
+
+ # Variable used to flag that we have a missing argument
+ argsValid = True
+
+ # Regexp is mandatory
+ if self.config.getRegexp() == None:
+ print "Error : Regexp is not defined. Please use --regexp flag, or --help to display help"
+ argsValid = False
+
+ # UpstreamURL is mandatory
+ if self.config.getUpstreamURL() == None:
+ print "Error : Upstream version page URL is not defined. Please use --upstream-url 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 execute(self, opts, args):
+
+ try:
+
+ # Initialize configuration
+ self.config.initialize(opts)
+
+ # Need a way to check that all options needed are available
+ self.checkArgument()
+
+ # Call the method in charge of retrieving upstream content
+ content = self.UrlContentRetrieve(self.config.getUpstreamURL())
+
+ # Search the strings matching the regexp passed through command line arguments
+ p = re.compile(self.config.getRegexp())
+ matches = p.findall(content)
+
+ # Check if we have found some results
+ if len(matches) == 0:
+ raise NoUpstreamVersionFoundException(self.config.getUpstreamURL(), self.config.getRegexp())
+ else:
+ newestVersion = matches.pop(0)
+ while len(matches) > 0:
+ newestVersion = self.CompareVersionAndGetNewest(newestVersion, matches.pop(0))
+
+ # At the end of the processing loop, we print the version
+ print newestVersion
+
+ # Exit after processing, eveythin gis ok, return true to the command processor
+ return True
+
+ except MissingArgumentException, (instance):
+
+ # Display a cool error message :)
+ print instance.parameter
+
+ # 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
+ return False
+
+ except NoUpstreamVersionFoundException, (instance):
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class GetUpstreamVersionListCommand(UpstreamWatchCommand):
+ """GetUpstreamVersionList command. This command retrieve the upstream web page and search for all the versions.
+ Version check is done by matching the regexp from the makefile on the page.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(GetUpstreamVersionListCommand, self).__init__(name)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkArgument(self):
+
+ # Variable used to flag that we have a missing argument
+ argsValid = True
+
+ # Regexp is mandatory
+ if self.config.getRegexp() == None:
+ print "Error : Regexp is not defined. Please use --regexp flag, or --help to display help"
+ argsValid = False
+
+ # UpstreamURL is mandatory
+ if self.config.getUpstreamURL() == None:
+ print "Error : Upstream version page URL is not defined. Please use --upstream-url 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 compareAndSortVersion(self, a, b):
+ """This function is a wrapper to the comparison function. CompareVersionAndGetNewest returns the string containing
+ the newest version of the two arguments. Since sort method used on list need to have an integer return value, this
+ wrapper do the call to CompareVersionAndGetNewest and returns an int
+ """
+
+ if self.CompareVersionAndGetNewest(a,b) == a:
+ return 1
+ else:
+ return -1
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def execute(self, opts, args):
+
+ try:
+
+ # Initialize configuration
+ self.config.initialize(opts)
+
+ # Need a way to check that all options needed are available
+ self.checkArgument()
+
+ # Call the method in charge of retrieving upstream content
+ content = self.UrlContentRetrieve(self.config.getUpstreamURL())
+
+ listURL = self.config.getUpstreamURL().split(' ')
+ print "Il y a %(num)d url dans la liste" % { 'num' : len(listURL) }
+
+ # Search the strings matching the regexp passed through command line arguments
+ p = re.compile(self.config.getRegexp())
+ matches = p.findall(content)
+
+ # Check if we have found some results
+ if len(matches) == 0:
+ raise NoUpstreamVersionFoundException(self.config.getUpstreamURL(), self.config.getRegexp())
+ else:
+ # Remove duplicated entries
+ myList = []
+ for version in matches:
+ myList.append(version)
+
+ l = list(set(myList))
+ l.sort(self.compareAndSortVersion)
+
+ # Print every version in the list
+ for version in l:
+ print version
+
+ # Exit after processing, eveythin gis ok, return true to the command processor
+ return True
+
+ except MissingArgumentException, (instance):
+
+ # Display a cool error message :)
+ print instance.parameter
+
+ # 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
+ 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.config.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.config.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.config.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.config.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.config.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.config.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.config.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.config.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.config.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.config.getTargetLocation() + "/upgrade_from_" + self.config.getCurrentVersion() + "_to_" + self.config.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.config.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.config.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.config.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.config.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 instance.parameter
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+ # 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
+
+ # 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
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class ReportPackageVersionCommand(UpstreamWatchCommand):
+ """ReportPackageVersion command. This command report and store in the database the values of version and date passed
+ by arguments to upstream watch. Unique key is the composed by garpath and catalog name. It means the same package can
+ lie into different path in the svn repository.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self, name):
+ super(ReportPackageVersionCommand, self).__init__(name)
+ self.conn = None
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def openDatabaseConnection(self):
+ """This method open a connection to the mysql database using value from the configuration parser. The result of
+ connect method is stored into a connection object
+ """
+
+ # Open the database connection
+ try:
+ self.conn = MySQLdb.connect(host = self.config.getDatabaseHost(),
+ passwd = self.config.getDatabasePassword(),
+ db = self.config.getDatabaseSchema(),
+ user = self.config.getDatabaseUser() )
+
+ except MySQLdb.Error, e:
+ msg = "Error %d: %s" % (e.args[0], e.args[1])
+ raise DatabaseConnectionException(msg)
+
+ # Check that the object we got in return if defiend
+ if self.conn == None:
+ # No, raise a DatabaseConnectionException
+ msg = "Unable to connect to database using host = %(host)s, db = %(db)s, user = %(user)s, passwd = %(passwd)% " % { "host" : self.config.getDatabaseHost(), "passwd" : self.config.getDatabasePassword(), "db" : self.config.getDatabaseSchema(), "user" : self.config.getDatabaseUser() }
+ raise DatabaseConnectionException(msg)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def closeDatabaseConnection(self):
+ """This method close the connection opened by openDatabaseConnection.
+ """
+
+ # Check that the connection object is valid
+ if self.conn :
+ # Yes, commit pending transactions
+ self.conn.commit()
+
+ # Close the connection
+ self.conn.close()
+ else:
+ # No, raise a DatabaseConnectionException
+ msg = "Unable to disconnect from the database. Connection objet is not defined"
+ raise DatabaseConnectionException(msg)
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def updateVersionInDatabase(self):
+ """This method updates the version in the database. First it checks for the package to update using a unique
+ key composed of gar svn path and catalog name. If not found the package is created, otherwise it is updated.
+ In both case, if data are writtent to the database, entries in history table are created.
+ """
+
+ try:
+ # Flag used to keep track of the fact we have created a new package. Used in some case to choose behavior
+ isNewlyCreatedPackage = False
+
+ # Check that the connection is defined
+ if self.conn == None:
+ # No, raise a DatabaseConnectionException
+ msg = "Unable to query the database. Connection objet is not defined"
+ raise DatabaseConnectionException(msg)
+
+ # Get a cursor object
+ cursor = self.conn.cursor(MySQLdb.cursors.DictCursor)
+
+ # First retrieve the id_pkg and deletion flag from the database
+ cursor.execute("select * from UWATCH_PKG_VERSION where PKG_CATALOGNAME = %s", (self.config.getCatalogName() ) )
+
+ # if more than one row is found, then report an error
+ if cursor.rowcount > 1:
+ msg = self.config.getCatalogName()
+ raise DuplicatePackageException(msg)
+
+ # If rowcount = 0 then the package does not exist. It has to be inserted in the database
+ if cursor.rowcount == 0:
+
+ # Insert the package in the package version table
+ cursor.execute("insert into UWATCH_PKG_VERSION (PKG_GAR_PATH, PKG_CATALOGNAME, PKG_NAME, PKG_GAR_VERSION, PKG_LAST_GAR_CHECK_DATE) \
+ values ( %s , %s , %s , %s , %s )" , (self.config.getGarPath(), self.config.getCatalogName(), \
+ self.config.getPackageName(), self.config.getGarVersion(), self.config.getExecutionDate() ) )
+
+ # Flag that we have created a package
+ isNewlyCreatedPackage = True
+
+ # Output some more information if verbose mode is activated
+ if self.config.getVerbose() == True:
+ print "Package %(pkg)s added to the database" % { 'pkg' : self.config.getCatalogName() }
+
+ # Now the package is inserted. Retrieve the newly inserted package and update other versions
+ cursor.execute("select * from UWATCH_PKG_VERSION where PKG_GAR_PATH = %s and PKG_CATALOGNAME = %s", \
+ (self.config.getGarPath(), self.config.getCatalogName() ) )
+
+ # Retrieve package information
+ pkg = cursor.fetchone()
+
+ # Test if the deleted flag is set
+ if pkg["PKG_IS_DELETED"] == 1:
+ # Yes thus package has to be undeleted
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_IS_DELETED = 0 where ID_PKG='%s'" , ( pkg["ID_PKG"] ) )
+
+ # Output some more information if verbose mode is activated
+ if self.config.getVerbose() == True:
+ print "Package %(pkg)s has been undeleted" % { 'pkg' : self.config.getCatalogName() }
+
+ # Test if the package has just been created. If yes the history line for gar version has to be inserted
+ if isNewlyCreatedPackage:
+ cursor.execute("insert into UWATCH_VERSION_HISTORY ( ID_PKG , HIST_VERSION_TYPE , HIST_VERSION_VALUE , HIST_VERSION_DATE ) values ( %s, %s, %s, %s)" , ( pkg["ID_PKG"], "gar", self.config.getGarVersion() , self.config.getExecutionDate() ) )
+
+ # In all cases (update or not) we update the last version check according to the argument
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_LAST_UPSTREAM_CHECK_DATE = %s , PKG_GAR_PATH = %s where ID_PKG= %s" , ( self.config.getExecutionDate(), self.config.getGarPath() , pkg["ID_PKG"] ) )
+
+ # Test if uwatch deactivated flag is set
+ if self.config.getUwatchDeactivated() == True:
+ # Yes thus package has to be deactivated
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_UWATCH_ACTIVATED='0' where ID_PKG= %s" , ( pkg["ID_PKG"] ) )
+ if self.config.getVerbose() == True:
+ print "%(pkg) uWatch is deactivated, updating database" % { 'pkg' : self.config.getCatalogName() }
+ else:
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_UWATCH_ACTIVATED='1' where ID_PKG= %s" , ( pkg["ID_PKG"] ) )
+ # Change execution status only if activated
+ if self.config.getUwatchError() == True:
+ # Yes thus package has to be updated
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_LAST_UPSTREAM_CHECK_STATUS='0' where ID_PKG= %s" , ( pkg["ID_PKG"] ) )
+ if self.config.getVerbose() == True:
+ print "%(pkg)s uWatch reported an error, updating database" % { 'pkg' : self.config.getCatalogName() }
+ else:
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_LAST_UPSTREAM_CHECK_STATUS='1' where ID_PKG= %s" , ( pkg["ID_PKG"] ) )
+ if self.config.getVerbose() == True:
+ print "%(pkg)s uWatch successfully ran, updating database" % { 'pkg' : self.config.getCatalogName() }
+
+ # Test if upstream version is passed
+ if self.config.getUpstreamVersion():
+ # In all cases (update or not) we update the last version check according to the argument
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_LAST_UPSTREAM_CHECK_DATE = %s , PKG_GAR_PATH = %s where ID_PKG= %s" , ( self.config.getExecutionDate(), self.config.getGarPath() , pkg["ID_PKG"] ) )
+
+ # Yes, compare current upstream version from commandline and database
+ if self.config.getUpstreamVersion() != pkg["PKG_UPSTREAM_VERSION"]:
+ # Yes thus package has to be updated
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_UPSTREAM_VERSION = %s where ID_PKG= %s" , ( self.config.getUpstreamVersion(), pkg["ID_PKG"] ) )
+ cursor.execute("insert into UWATCH_VERSION_HISTORY ( ID_PKG , HIST_VERSION_TYPE , HIST_VERSION_VALUE , HIST_VERSION_DATE ) \
+ values ( %s, %s, %s, %s)" , ( pkg["ID_PKG"], "upstream", self.config.getUpstreamVersion() , self.config.getExecutionDate() ) )
+
+ # Output some more information if verbose mode is activated
+ if self.config.getVerbose() == True:
+ print "Upgrading %(pkg)s upstream version from %(current)s to %(next)s" % { 'pkg' : self.config.getCatalogName(), \
+ 'next' : self.config.getUpstreamVersion() , 'current' : pkg["PKG_UPSTREAM_VERSION"] }
+ else:
+ # Output some more information if verbose mode is activated
+ if self.config.getVerbose() == True:
+ print "%(pkg) GAR version is up to date (%(current)s)" % { 'pkg' : self.config.getCatalogName(), 'current' : self.config.getUpstreamVersion() }
+
+ # Test if gar version is passed (it is mandatory to have a value in database)
+ if self.config.getGarVersion():
+ # In all cases (update or not) we update the last version check according to the argument
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_LAST_GAR_CHECK_DATE = %s where ID_PKG= %s" , ( self.config.getExecutionDate(), pkg["ID_PKG"] ) )
+
+ # Yes, compare current gar version from commandline and database
+ if self.config.getGarVersion() != pkg["PKG_GAR_VERSION"]:
+ # Yes thus package has to be updated
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_GAR_VERSION = %s where ID_PKG= %s" , ( self.config.getGarVersion(), pkg["ID_PKG"] ) )
+ cursor.execute("insert into UWATCH_VERSION_HISTORY ( ID_PKG , HIST_VERSION_TYPE , HIST_VERSION_VALUE , HIST_VERSION_DATE ) \
+ values ( %s, %s, %s, %s)" , ( pkg["ID_PKG"], "gar", self.config.getGarVersion() , self.config.getExecutionDate() ) )
+
+ # Output some more information if verbose mode is activated
+ if self.config.getVerbose() == True:
+ print "Upgrading %(pkg)s gar version from %(current)s to %(next)s" % { 'pkg' : self.config.getCatalogName(), \
+ 'next' : self.config.getGarVersion() , 'current' : pkg["PKG_GAR_VERSION"] }
+
+ # Before closing the connection, there is a last thing to do... storing uwatch configuration into the database
+
+ # Yes, compare current gar version from commandline and database
+ cursor.execute("update UWATCH_PKG_VERSION set PKG_GAR_DISTFILES = %s, PKG_UFILES_REGEXP = %s, PKG_UPSTREAM_MASTER_SITES = %s, PKG_UWATCH_LAST_OUTPUT = %s where ID_PKG= %s" , ( self.config.getGarDistfiles(), self.config.getRegexp(), self.config.getUpstreamURL(), self.config.getUwatchOutput(), pkg["ID_PKG"] ) )
+
+ # Close the cursor on the database
+ cursor.close()
+
+ except MySQLdb.Error, e:
+ msg = "Error %d: %s" % (e.args[0], e.args[1])
+ raise DatabaseConnectionException(msg)
+
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def checkArgument(self):
+
+ # Variable used to flag that we have a missing argument
+ argsValid = True
+
+ # Gar path is mandatory
+ if self.config.getGarPath() == None:
+ print "Error : Gar path is not defined. Please use --gar-path flag, or --help to display help"
+ argsValid = False
+
+ # Gar distfiles is mandatory
+ if self.config.getGarDistfiles() == None:
+ print "Error : Gar distfiles is not defined. Please use --gar-distfiles flag, or --help to display help"
+ argsValid = False
+
+ # Gar distfiles is mandatory
+ if self.config.getUwatchOutput() == None:
+ print "Error : uWatch output is not defined. Please use --uwatch-output flag, or --help to display help"
+ argsValid = False
+
+ # Catalog name is mandatory
+ if self.config.getCatalogName() == None:
+ print "Error : Catalog name is not defined. Please use --catalog-name flag, or --help to display help"
+ argsValid = False
+
+ # Package name is mandatory
+ if self.config.getPackageName() == None:
+ print "Error : Package name is not defined. Please use --package-name flag, or --help to display help"
+ argsValid = False
+
+ # Execution date is mandatory
+ if self.config.getExecutionDate() == None:
+ print "Error : Execution date is not defined. Please use --execution-date flag, or --help to display help"
+ argsValid = False
+
+ # Gar version is mandatory, other version are optional. Gar the version is the only mandatory in the database
+ # It has to be passed in argument in case the package does not exist yet
+ if self.config.getGarVersion() == None:
+ print "Error : Gar version is not defined. Please use --gar-version flag, or --help to display help"
+ argsValid = False
+
+ # Database schema is mandatory
+ if self.config.getDatabaseSchema() == None:
+ print "Error : Database schema is not defined. Please define the value in the ~/.uwatchrc file, use --database-schema flag, or --help to display help"
+ argsValid = False
+
+ # Database host is mandatory
+ if self.config.getDatabaseHost() == None:
+ print "Error : Database host is not defined. Please define the value in the ~/.uwatchrc file, use --database-host flag, or --help to display help"
+ argsValid = False
+
+ # Database user is mandatory
+ if self.config.getDatabaseUser() == None:
+ print "Error : Database user is not defined. Please define the value in the ~/.uwatchrc file, use --database-user flag, or --help to display help"
+ argsValid = False
+
+ # Database password is mandatory
+ if self.config.getDatabasePassword() == None:
+ print "Error : Database password is not defined. Please define the value in the ~/.uwatchrc file, use --database-password flag, or --help to display help"
+ argsValid = False
+
+ # Regexp is mandatory
+ if self.config.getRegexp() == None:
+ print "Error : Regexp is not defined. Please use --regexp flag, or --help to display help"
+ argsValid = False
+
+ # UpstreamURL is mandatory
+ if self.config.getUpstreamURL() == None:
+ print "Error : Upstream version page URL is not defined. Please use --upstream-url 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 execute(self, opts, args):
+
+ try:
+
+ # Initialize configuration
+ self.config.initialize(opts)
+
+ # Need a way to check that all options needed are available
+ self.checkArgument()
+
+ # Connection to the database
+ self.openDatabaseConnection()
+
+ # Connection to the database
+ self.updateVersionInDatabase()
+
+ # Connection to the database
+ self.closeDatabaseConnection()
+
+ # Exit after processing, eveythin gis ok, return true to the command processor
+ return True
+
+ except MissingArgumentException, (instance):
+
+ # Display a cool error message :)
+ print instance.parameter
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+ except InvalidArgumentException, (instance):
+
+ # Display a cool error message :)
+ print instance.parameter
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+ except DatabaseConnectionException, (instance):
+
+ # Display a cool error message :)
+ print instance.message
+
+ # Exits through exception handling, thus return false to the command processor
+ return False
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+#
+class CommandProcessor(object):
+ """This class receive commands from the main loop and forward call to concrete command.
+ """
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def __init__(self):
+ """Initialize the objects in charge of concrete command processing. Each object instance are stored in a
+ map using a key which is the action verb used on the command line.
+ """
+
+ # Defines the map storing the concrete commands
+ self.commandArray = {}
+
+ # Creates all the concrete commands
+ cmd = CheckUpstreamCommand("check-upstream")
+ self.commandArray[cmd.getName()] = cmd
+
+ cmd = GetUpstreamLatestVersionCommand("get-upstream-latest-version")
+ self.commandArray[cmd.getName()] = cmd
+
+ cmd = GetUpstreamVersionListCommand("get-upstream-version-list")
+ self.commandArray[cmd.getName()] = cmd
+
+ cmd = UpgradeToVersionCommand("upgrade-to-version")
+ self.commandArray[cmd.getName()] = cmd
+
+ cmd = ReportPackageVersionCommand("update-package-version-database")
+ self.commandArray[cmd.getName()] = cmd
+
+ # -----------------------------------------------------------------------------------------------------------------
+
+ def execute(self, opts, arguments):
+ """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
+ if len(arguments) == 0:
+ print "Error : no action supplied"
+ return 1
+
+ # The first element in the arguments array is the action verb. Retrieve the command
+ # using action verb as key
+ if self.commandArray.has_key(arguments[0]):
+ res = self.commandArray[arguments[0]].execute(opts, arguments)
+ if res:
+ return 0
+ else:
+ return 1
+ else:
+ print "Error : %(action)s action is not supported" % { 'action' : arguments[0] }
+ return 2
+
+
+class UwatchRegexGenerator(object):
+
+ WS_RE = re.compile(r'\s+')
+ DIGIT_RE = re.compile(r'\d+')
+ DIGIT_REMOVAL_RE = re.compile(r'\d+(?:\.\d+)*[a-z]?')
+ DIGIT_MATCH_MAKE_RE_1 = r'(\d+(?:\.\d+)*[a-z]?)'
+ DIGIT_MATCH_MAKE_RE_2 = r'(\d+(?:\.\d+)*)'
+ ARCHIVE_RE = re.compile(r"\.(?:tar(?:\.(?:gz|bz2))|tgz)?$")
+
+ def _ChooseDistfile(self, file_list):
+ # First heuristic: files with numbers are distfiles
+ for f in file_list:
+ if self.ARCHIVE_RE.search(f):
+ return f
+ for f in file_list:
+ if self.DIGIT_RE.search(f):
+ return f
+
+ def _SeparateSoftwareName(self, catalogname, filename):
+ """Separate the software name from the rest of the file name.
+
+ Software name sometimes contains digits, which we don't want to
+ include in the regexes.
+ """
+ # The first approach is to split by '-'
+ assert filename
+ parts = filename.split('-')
+ if self._CanBeSoftwarename(parts[0], catalogname):
+ return parts[0], '-' + '-'.join(parts[1:])
+ return '', filename
+
+ def _SeparateArchiveName(self, filename):
+ if self.ARCHIVE_RE.search(filename):
+ first_part = self.ARCHIVE_RE.split(filename)[0]
+ archive_part = ''.join(self.ARCHIVE_RE.findall(filename))
+ return first_part, archive_part
+ return filename, ''
+
+ def _CanBeSoftwarename(self, s, catalogname):
+ if s == catalogname:
+ return True
+ # This is stupid. But let's wait for a real world counterexample.
+ return True
+
+ def GenerateRegex(self, catalogname, distnames):
+ dist_list = self.WS_RE.split(distnames)
+ dist_file = self._ChooseDistfile(dist_list)
+ if not dist_file:
+ return []
+ softwarename, rest_of_filename = self._SeparateSoftwareName(
+ catalogname, dist_file)
+ rest_of_filename, archive_part = self._SeparateArchiveName(rest_of_filename)
+ no_numbers = self.DIGIT_REMOVAL_RE.split(rest_of_filename)
+ regex_list = []
+ regex_list.append(softwarename + self.DIGIT_MATCH_MAKE_RE_1.join(no_numbers) + archive_part)
+ regex_list.append(softwarename + self.DIGIT_MATCH_MAKE_RE_2.join(no_numbers) + archive_part)
+ return regex_list
+
+
+
+# ---------------------------------------------------------------------------------------------------------------------
+#
+# Fonction principale
+#
+def main():
+ """Program main loop. Process args and call concrete command action.
+ """
+
+ # Create the command processor object
+ commandProcessor = CommandProcessor()
+
+ # Parse command line arguments
+ cliParser = CommandLineParser()
+
+ # Call the command line parser
+ (opts, args) = cliParser.parse()
+
+ # Call the execute method on the command processor. This method is in charge to find the concrete command
+ return commandProcessor.execute(opts, args)
+
+# Exit with main return code
+if __name__ == '__main__':
+ res = main()
+ sys.exit(res)
+
Modified: csw/mgar/gar/v2/categories/cpan/category.mk
===================================================================
--- csw/mgar/gar/v2/categories/cpan/category.mk 2011-02-13 12:55:58 UTC (rev 13270)
+++ csw/mgar/gar/v2/categories/cpan/category.mk 2011-02-13 13:14:18 UTC (rev 13271)
@@ -28,8 +28,7 @@
SPKG_SOURCEURL := $(SPKG_SOURCEURL)/~$(call TOLOWER,$(AUTHOR))
# We define upstream file regex so we can be notifed of new upstream software release
-UFILES_REGEX ?= $(NAME)-(\d+(?:\.\d+)*).tar.gz
-USTREAM_MASTER_SITE ?= $(SPKG_SOURCEURL)
+UPSTREAM_MASTER_SITES ?= $(SPKG_SOURCEURL)
$(foreach P,$(PACKAGES),$(eval _CATEGORY_SPKG_DESC_$P = $$(NAME): $$(or $$(SPKG_DESC_$P),$$(SPKG_DESC))))
_CATEGORY_PKGINFO = echo "PERL_MODULE_NAME=$(NAME)";
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