# # P4Triggers.py # # # Copyright (c) 2008, 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. # # $Id: //guest/sven_erik_knop/P4Pythonlib/triggers/P4Triggers.py#9 $ # # # # Base class for all Python based P4 triggers # from __future__ import print_function import P4 from datetime import datetime import sys 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] if len(desc["fileSize"]) > n: dr.fileSize = int(desc["fileSize"][n]) else: dr.fileSize = 0 if len(desc["digest"]) > n: dr.digest = desc["digest"][n] else: dr.digest = "" self.files.append( df ) self.jobs = {} if "job" in desc: for n, j in enumerate( desc[ "job" ] ): self.jobs[j] = desc[ "jobstat" ][ n ] DEFAULT_LOG = "p4triggers.log" class P4Trigger: """Base class for Perforce Triggers""" def __init__( self, **kargs): """Constructor for P4Trigger. Keyword arguments are passed to the P4.P4() instance used""" logFileName = DEFAULT_LOG if "log" in kargs: logFileName = kargs["log"] del kargs["log"] self.log = open(logFileName, "a+") self.p4 = P4.P4(**kargs) def parseChange( self, changeNo ): try: self.p4.connect() self.setUp() self.change = self.getChange( changeNo ) return 0 if self.validate() else 1 except P4.P4Exception as err: return self.reportError(err) except: print("Exception during trigger execution: %s %s %s" % sys.exc_info(), file=self.log) return 1 return 0 def getChange( self, changeNo ): return P4Change( self.p4.run_describe( changeNo )[0] ) def validate(self): return True # method that sublasses can overwrite in order to complete the setup of P4 connection def setUp( self ): pass # # Method to send a message to the user. Just writes to stdout, but it's # nice to encapsulate that here. # def message( self, msg ): print( msg ) def reportError( self , err ): """Method to encapsulate error reporting to make sure all errors are reported in a consistent way""" print( "Error during trigger execution:", file=self.log ) self.reportP4Errors( self.log ) print( err, file=self.log) print( sys.exc_info(), file=self.log ) # return message to the user self.errorMessage() self.reportP4Errors( sys.stdout ) print( err ) return 1 def reportP4Errors( self, channel ): for e in self.p4.errors: print( "ERROR: %s" % e, file=channel ) for w in self.p4.warnings: print( "WARNING: %s" % w, file=channel ) return 1 # Default message for when things go wrong 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 """
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#9 | 23692 | Sven Erik Knop |
Simple trigger that logs sizes of submitted changelists. Invoke with python3 SizeLogger.py <config> <change> where config is the filename of a config file containing the following two sections: -------------- [P4] port=1666 user=tom password=A154ABF11F094B5D288CA0BE9F6F1086 [Log] logfile=mylogfile.log -------------- The P4 connection will use the standard defaults for any parameters not defined (such P4PORT, P4USER) The default log file is called 'sizes.log' Since this trigger is based on the standard P4Triggers.py, it will also create a separate log file (p4triggers.log) for any errors that occur from a connection failure or other exception thrown. Install this trigger like this in the triggers table: SizeLogger change-commit //depot/path/... "python3 SizeLogger.py configfile.name %change%" You need to have P4Triggers.py in the same path as the SizeLogger.py file. --------- This trigger uses 'p4 describe' to collect all files in the change, filters out any files that do not have 'add' or 'edit' action, and then sums up the remaining file sizes - so branches and renames are ignored. The output in the log file is in JSON format for easy parsing. --------- I also updated P4Triggers.py to add size and digest to the internal stored change |
||
#8 | 21316 | Sven Erik Knop |
Enhanced error output to print the actual exception. Should make debugging a bit easier. |
||
#7 | 8357 | Sven Erik Knop | Fixed missing print -> print() function for Python 3 | ||
#6 | 8179 | Sven Erik Knop |
Added filefilter to the CheckCaseTrigger. A filefilter is added as an additional key on the command line: filefilter=path-to-my-filter-file It should contain valid Perforce depot syntax lines, which are treated as exceptions. Changed files that match any of the lines in the filefilter will be allowed to submit even if they break the case consistency. This is for cases where some files are Unix-specific and need to be able to store two files or directories that only differ by case - as an exception. Also fixed a tabbing problem in the P4Triggers.py file. |
||
#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. | ||
# | Change | User | Description | Committed | |
#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. |