|
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!
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.
|