2 Trigger Techniques
This section discusses techniques used to create triggers of the different types. Some techniques have applicability to contexts other than triggers, but all are relevant to
triggers. The following techniques build upon the techniques presented in [Bowles2005], but are commonly or exclusively used in the trigger context.
2.1 Exit codes
This is an almost trivially simple technique that applies solely to triggers. The trigger communicates whether the associated action is successful or not through its exit status. An exit status of 0 indicates success. Any other exit status indicates failure. The exact value of a non-zero exit status can not be meaningfully used. Some languages provide implicit non-zero exit status with common error handling facilities, such as die() in Perl.
if( $success )
die “Operation failed. Fix problem.”;
Figure 1. Perl example of trigger exit status
2.2 Error messages
Ordinary scripting delivers all output messages to the caller. Triggers can only provide custom feedback to the user through messages that accompany error exit status. Messages output during script execution will be discarded when the trigger is successful. Figure 1 shows a message that will be sent back to the user on failure.
2.3 Multiple different actions on same files
With triggers you have the ability to invoke multiple triggers on the same object until one of the triggers fails. An object is a particular file or file path for pre-submit triggers, or a particular form type for form triggers. This is accomplished by defining multiple triggers with different names on the same object definition. Triggers fire in the order presented in the triggers definition.
success1 submit //… “ruby submit_trigger.rb”
failure1 submit //… “ruby always_fails.rb”
success2 submit //… “ruby another_trigger.rb”
Figure 2. Example of multiple submit triggers on the same path.
In the above example, the third trigger will never fire because the second trigger always fails.
2.4 Same action on multiple file patterns
A single trigger also has the ability to operate on multiple file patterns. Multiple lines with the same name as shown in Figure 3 are considered to be a single trigger.
doit submit //….h “python trigger1.py”
doit submit //….c “python trigger2.py”
Figure 3. Example of a single multi-line trigger.
Note that only the first command is significant. If a file matches the pattern for the second line, the command from the first line will be executed.
2.5 Tagged output
A technique that is applicable to all scripting is the use of tagged output in general or marshaled output from Python or, if comfortable with undocumented functionality, Ruby. With the appropriate global option flag, p4 will output the results of commands in marshaled object format for the chosen language. Figure 4 and Figure 5 show
examples using Python and Ruby, respectively.
$ p4 –G label –o mylabel | processlabel.py
Figure 4. Example of Python marshaled object output.
$ p4 –R label –o mylabel | processlabel.rb
Figure 5. Example of the undocumented Ruby marshaled object output. An example of handling marshaled output in Python is given in [Goldstone2005]. More
information on tagged output is presented in [Bowles2005]. Tagged output eliminates the need to write stateful parsers to process commands.
2.6 Form modification
Another generic technique that is commonly used in triggers is form parsing and
modification. Tagged output, discussed in section 2.5, simplifies the task. The essence of
the technique is to use the -o flag to output the form and the -i flag to input the form. A
coarse example using Python is shown in .
$ p4 –G client -o | transformit.py | \
p4 -G client -i
Figure 6. High-level example of form modification.
2.7 Initiating follow-on processing
Although this technique is applicable to many contexts, it is particularly useful with“save” and “commit” triggers. Neither of these trigger types have the ability to modify
forms or fail an operation. They faithfully execute after the completion of the operation, providing a perfect opportunity to initiate further processing. This processing must not be time-consuming, as they still prevent the operation from returning until they finish. This approach is also useful when the follow-on processing would lock Perforce databases that would already be locked as a natural consequence of the triggering operation. Dissociating the processing from the initial operation avoids deadlock conditions. There are two primary techniques to accomplish this with numerous implementations. The first technique starts the processing immediately but does not wait for completion as with launching a sub-process. The second technique requires a means of sending a signal or “opening the gate” by setting some state that another process will recognize as a cue toprocess. Note that the latter technique includes review daemons, which involves another context and does not require cooperation from a trigger. Specific implementations are left to the experience and ingenuity of the reader.
2.8 Coordinated trigger actions
Triggers execute in the following order: out, in, save, submit, content, commit. Obviously not all trigger types execute in all situations. Pre-submit triggers only execute on
submissions. Out triggers do not execute when submitting a numbered changelist because the form has already been generated. The order of execution allows triggers to coordinate actions in a manner appropriate to their individual strengths and weaknesses. As an optimization, a form trigger that was already processing a form could cache state information that would be used by a submit trigger. For example, certain reviewers may be required in certain areas of the repository or for certain users. The parsing of reviewer information could occur once during the in trigger and be used during the submit trigger instead of being re-parsed. As another example, whether a build should occur may depend on the nature of the change recognized by a content trigger, but the policy for the decision to build or not could be encapsulated and acted upon in a commit trigger from where the build should be launched.