P4Triggers.py #5

  • //
  • guest/
  • perforce_software/
  • sdp/
  • main/
  • Server/
  • Unix/
  • p4/
  • common/
  • bin/
  • triggers/
  • P4Triggers.py
  • View
  • Commits
  • Open Download .zip Download (7 KB)
#
# P4Triggers.py
#
# Version 2.0.4
#
# Copyright (c) 2008-2016, Perforce Software, Inc.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#
# 2.  Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# # Base class for all Python based P4 triggers
#

from __future__ import print_function

import P4
from datetime import datetime
import sys
import os
import logging
import traceback

# If working on a server with the SDP, the 'LOGS' environment variable contains
# the path the standard logging directory.  The '-L <logfile>' argument shoudl be
# specified in non-SDP environments.
LOGDIR = os.getenv('LOGS', '/p4/1/logs')

DEFAULT_LOG_FILE = "%s/p4triggers.log" % LOGDIR
DEFAULT_VERBOSITY = 'DEBUG'

LOGGER_NAME = 'P4Triggers'


class P4Change:
    """Encapsulates a Perforce change. Basically a pretty wrapping around p4.run_describe()"""

    def __init__(self, desc):
        self.change = desc["change"]
        self.user = desc["user"]
        self.client = desc["client"]
        self.desc = desc["desc"]
        self.time = datetime.utcfromtimestamp(int(desc["time"]))
        self.status = desc["status"]

        self.files = []
        if "depotFile" in desc:
            for n, d in enumerate(desc["depotFile"]):
                df = P4.DepotFile(d)
                dr = df.new_revision()
                dr.type = desc["type"][n]
                dr.rev = desc["rev"][n]
                dr.action = desc["action"][n]
                self.files.append(df)

        self.jobs = {}
        if "job" in desc:
            for n, j in enumerate(desc["job"]):
                self.jobs[j] = desc["jobstat"][n]


class P4Trigger(object):
    """Base class for Perforce Triggers"""

    def __init__(self, *args, **kwargs):
        """Constructor for P4Trigger.
           Keyword arguments are passed to the P4.P4() instance used"""
        kwargs['charset'] = 'none'
        # API Levels are defined here: http://answers.perforce.com/articles/KB/3197
        # Ensure this does not exceed the value for the P4Python version used.
        # API Level 79 is for p4d 2015.2.
        kwargs['api_level'] = 79
        self.p4 = P4.P4(**kwargs)
        self.options = None
        self.logger = None
        self.change = None

    def add_parse_args(self, parser, default_log_file=None, default_verbosity=None):
        """Default trigger argumenst - common to all triggers
        :param default_verbosity:
        :param default_log_file:
        :param parser:
        """
        if not default_log_file:
            default_log_file = DEFAULT_LOG_FILE
        if not default_verbosity:
            default_verbosity = DEFAULT_VERBOSITY
        parser.add_argument('-p', '--port', default=None,
                            help="Perforce server port - set using %%serverport%%. Default: $P4PORT")
        parser.add_argument('-u', '--user', default=None, help="Perforce user. Default: $P4USER")
        parser.add_argument('-L', '--log', default=DEFAULT_LOG_FILE, help="Default: " + default_log_file)
        parser.add_argument('-v', '--verbosity',
                            nargs='?',
                            const="INFO",
                            default=default_verbosity,
                            choices=('DEBUG', 'WARNING', 'INFO', 'ERROR', 'FATAL'),
                            help="Output verbosity level. Default is: " + default_verbosity)

    def init_logger(self, logger_name=None):
        if not logger_name:
            logger_name = LOGGER_NAME
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(self.options.verbosity)
        logformat = '%(levelname)s %(asctime)s %(filename)s %(lineno)d: %(message)s'
        logging.basicConfig(format=logformat, filename=self.options.log, level=self.options.verbosity)

    def setupP4(self):
        if self.options.port:
            self.p4.port = self.options.port
        if self.options.user:
            self.p4.user = self.options.user
        self.p4.logger = self.logger
        self.logger.debug("P4 port: '%s', user: '%s'" % (self.p4.port, self.p4.user))

    def parseChange(self, changeNo):
        try:
            self.p4.connect()
            self.setUp()

            self.change = self.getChange(changeNo)
            return 0 if self.validate() else 1
        except Exception:
            return self.reportException()

    def getChange(self, changeNo):
        return P4Change(self.p4.run_describe(changeNo)[0])

    def validate(self):
        """Intended to be implemented in sub-class"""
        return True

    # method that sublasses can overwrite in order to complete the setup of P4 connection
    def setUp(self):
        pass

    def message(self, msg):
        """Method to send a message to the user. Just writes to stdout, but it's
        nice to encapsulate that here.
        :param msg: """
        self.logger.info(msg)
        print(msg)

    def errorMessage(self):
        return """
An error was encountered during trigger execution. Please
contact your Perforce administrator and ask them to
investigate the cause of this error in %s
""" % self.options.log

    def reportException(self):
        """Method to encapsulate error reporting to make sure
           all errors are reported in a consistent way"""
        exc_type, exc_value, exc_tb = sys.exc_info()
        self.message("Exception during trigger execution: %s %s %s" % (exc_type, exc_value, exc_tb))
        self.reportP4Errors()
        self.logger.error("called from:\n%s", "".join(traceback.format_exception(exc_type, exc_value, exc_tb)))

        # return message to the user
        self.message(self.errorMessage())
        return 1

    def reportP4Errors(self):
        lines = []
        for e in self.p4.errors:
            lines.append("P4 ERROR: %s" % e)
        for w in self.p4.warnings:
            lines.append("P4 WARNING: %s" % w)
        if lines:
            self.message("\n".join(lines))
# Change User Description Committed
#8 27331 C. Thomas Tyler Released SDP 2020.1.27325 (2021/01/29).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#7 25245 C. Thomas Tyler Released SDP 2019.1.25238 (2019/03/02).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#6 23331 C. Thomas Tyler Released SDP 2017.4.23329 (2017/12/05).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#5 23006 C. Thomas Tyler Released SDP 2017.3.23003 (2017/10/19).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#4 21532 C. Thomas Tyler Released SDP 2016.2.21528 (2017/01/14).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#3 21338 C. Thomas Tyler Released SDP 2016.2.21328 (2016/12/16).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#2 21128 C. Thomas Tyler Released SDP 2016.2.21123 (2016/11/22).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
#1 21105 C. Thomas Tyler Released SDP 2016.2.21103 (2016/11/21).
Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'.
//guest/perforce_software/sdp/dev/Server/Unix/p4/common/bin/triggers/P4Triggers.py
#2 21101 C. Thomas Tyler SDP-ified; changed type to xtext, removed RCS keywoard.
#1 21100 C. Thomas Tyler Branched P4Triggers.py
//guest/robert_cowham/perforce/utils/triggers/P4Triggers.py
#2 19939 Robert Cowham Update with latest changes by Sven etc.
#1 7531 Robert Cowham Personal branch
//guest/sven_erik_knop/P4Pythonlib/triggers/P4Triggers.py
#5 7428 Sven Erik Knop Error output from Perforce is now written to the log file instead of stderr.
#4 7379 Sven Erik Knop Added output to a log file.
The default is the send output to p4triggers.log in the P4ROOT directory, this can be overridden with the parameter log=<path>
Also, errors now cause the trigger to fail with sensible output first.
#3 7372 Sven Erik Knop Rollback Rename/move file(s).
To folder "perforce" is needed.
#2 7370 Sven Erik Knop Rename/move file(s) again - this time to the right location inside a perforce directory.
#1 7367 Sven Erik Knop New locations for the Python triggers.
//guest/sven_erik_knop/perforce/P4Pythonlib/triggers/P4Triggers.py
#1 7370 Sven Erik Knop Rename/move file(s) again - this time to the right location inside a perforce directory.
//guest/sven_erik_knop/P4Pythonlib/triggers/P4Triggers.py
#1 7367 Sven Erik Knop New locations for the Python triggers.
//guest/sven_erik_knop/triggers/P4Triggers.py
#1 6413 Sven Erik Knop Added some P4Python-based Perforce triggers.

P4Triggers.py is the based class for change trigger in Python modelled on
Tony Smith's Ruby trigger with the same name.

CheckCaseTrigger.py is a trigger that ensures that no-one enters a file
or directory with a name only differing by case from an existing file. This
trigger is Unicode aware and uses Unicode-comparison of file names, so it
can be used on nocase-Unicode based Perforce servers, which cannot catch
the difference between, say, "�re" and "�re" at the moment.

clienttrigger.py is a simple trigger that modifies the option "normdir" to
"rmdir" for new client specs only. It is meant as a template to create more
complex default settings like standard views.