Writing good Perforce triggers, and, more importantly, debugging them in live use, turns out to be one of those things that seems simple but has lots of tricky issues that can lead to lots of time being wasted.
In spite of thinking that I understood lots of the issues, I still spent a couple of hours recently debugging a problem that turned out to be a combination of environment and password issues. This was particularly annoying as I had rather though I knew about this stuff (and indeed have advised people over the years about it!), and yet was blindsided and caught out by some issues I had forgotten about or not thought through deeply enough.
I reserve the right to revisit this subject more than once in the future with further insights and news…
Assume Nothing About The Environment!
The classic approach to triggers is to write a nice script (Python or Ruby for me these days – no Perl, though just occasionally I miss it!) and debug it by running with the appropriate parameters from the command line (e.g. create a pending changelist and pass in the pending changelist number). This does indeed tend to turn up a number of issues, but the good thing is you can usually debug them with the appropriate command (<rant> why does python require you to execute pdb.py which isn’t by default put in the path on Windows machines, and why does Ruby not learn from Perl and for example use -d as a parameter to debug things instead of “-rdebug” – very unobvious!</rant>).
The major problem turns out to be the fact that the trigger is executed by the Perforce server process and may have a very different environment to what you might think as you run a “login” session. One sort of expects this on Unix, but on Windows it can be particularly surprising how little is in the environment due to the username that the Perforce process is running in when it is running as a service (default installation on Windows).
Thus the first rule of trigger writing is “assume nothing about the environment!“.
It is very easy to forget this and assume very simple things, like:
- P4PORT is always defined
- P4USER is always defined
- failures of individual p4 commands within the trigger will be obvious
Thus immediate recommendations are:
- Give full pathnames to executables. For example, “/usr/bin/ruby” or “C:\ruby\bin ruby.exe” as the initial parameter for the ruby script, rather than assuming that “ruby” or “python” or whatever will always be in the PATH of the user executing the command.
- When in doubt (I’m generally always in doubt) give full pathnames to scripts too.
- Pass in as parameters the p4port and any other parameters to be used rather than expecting them to be already present in the environment.
- Within the script, explicitly add any extra directories to the search path for commands such as “import p4″ in Python or “require ‘P4′ ” in Ruby or any equivalent import-type statement, unless you are absolutely sure that the imported libraries are globally installed on the machine your are working with. Don’t assume the same directory as the trigger script itself is in is in the path unless you can prove it.
- Trap and print to stdout (or stderr which goes to the p4d server log file) any errors/stack traces including exceptions from your p4 interface to aid hunting out problems. This is much easier to say than to do!
Passwords Cause Problems
In the good old days, before “p4 login” was even a twinkle in Christopher’s eye, you could write your trigger assuming super user privileges (says in Yorkshire accent “we had it tough – could only dream of admin privileges in those days”) and everything would work.
Life became substantially more complicated with security level 3 and login being required. Commands failed due to not being logged in, and this turned out to be a bit of a bugger (’scuse my French) to work out (why it had failed that is).
Received wisdom is “run your triggers as a special trigger/admin user, put that user in a special group with timeout of some very large number, log them in manually and all will be sweetness and light”.
The interesting thing about this approach is that it often works, but as I discovered recently, can flatter to deceive. The problem I had was that the super user was indeed in a special “long timeout” group, and logged in on the same box (generating a suitable ticket). However, as I discovered only after some hair was torn out, the P4PORT that the user was logged in under was different to that used by trigger and thus the P4TICKET file entry was also different and the existing “login” had had no effect and my trigger was unfortunately failing silently.
Thus P4PORT=localhost:1666 where localhost=some_server.some_company.com will not work if the superuser is logged in using P4PORT=some_server.some_company.com:1666, since the latter is what will be in P4TICKET and the former will not be found and thus commands will fail. Be warned and expect/check for this! [Note: this was fixed in p4d 2007.2]
When in doubt print out the environment within your script (via some sort of debug parameter).
Belt and Braces
My current intentions on this front are to produce a trigger framework that helps detect the above problems, and helps both avoid them and, when necessary, debug them in a (relatively) painless manner. This, at the moment of writing, is a work in progress, but I hope to be able to share it with the wider Perforce community as it emerges into the glare of publicity. I do reserve the right to retain the right of surprise to add some slight spice to my upcoming presentation at the European Perforce User Conference on the 19th September (in London).
Update: hopefully will be able to share a rework/expansion of Tony Smith’s P4Trigger.rb framework which addresses some of the above issues fairly shortly – seems to be working at a client – time will tell – but fairly quickly.
Future topics will include ideas on test frameworks etc.



