bk triggers(7.3ce) BitKeeper User's Manual bk triggers(7.3ce) NAME bk triggers - using BitKeeper event triggers DESCRIPTION BitKeeper supports a variety of trigger types. Triggers are simple programs, usually shell scripts, which may be called before and/or after certain events, such as a commit, pull, push, resolve, etc. Triggers can be used for event notification and/or to implement control over the events themselves. When a trigger is called, it is called with the current working direc- tory set to the root of the repository. Note that in the case of incoming data, the root of the repository is defined as the RESYNC directory. If an incoming changeset adds or updates a trigger, the incoming trig- ger is not the trigger fired, with the exception of the post-incoming trigger. The trigger already present in the repository, if any, is the trigger used. This arrangement is for security reasons; incoming changes could be malicious or ill advised and a prudent repository man- ager may have developed triggers to look for problems. If the incoming data contained a new or modified trigger, and that trigger was run, triggers could not be used to implement security or other policies at the repository boundary. TRIGGER NAMES When an event occurs, if there exists a file BitKeeper/trig- gers/event_class in the repository root that corresponds to the event, BitKeeper will execute that trigger. For example, if there is a push from repository B going into repository A and repository A has a file BitKeeper/triggers/pre-incoming, the pre-incoming script will be run before the push event applies to repository A. All triggers for a particular class must be named with the class name and prefix, for example pre-incoming-ok. More than one trigger per event class is allowed; each trigger program has the class name and prefix and a suffix of your choosing. The trigger programs are sorted and run alphabetically (C locale sort order). If there are multiple pre- triggers (see below), the first trigger to exit with a non-zero status will halt the trigger processing. If there are pre- triggers that must always be run, they must be named such that their name will sort earlier than any other trigger of that class. In order to avoid name space conflicts, the typical approach is to use the reason for the trigger as the suffix, i.e., post-incoming.mail post-incoming.regression-test Files ending in ~ are ignored to avoid editor backup files. TRIGGER PATHS By default, triggers are stored in the repository under the Bit- Keeper/triggers/ directory and this is the only directory searched when looking for triggers. More than one triggers directory may be used by setting the triggers variable. The format is one or more paths sepa- rated by a vertical bar, each path has "BitKeeper/triggers" appended to it and the resulting path is scanned for triggers. For example, if you wanted to run triggers from /etc/BitKeeper/triggers and from the repos- itories' BitKeeper/triggers, set the variable as follows in your con- figuration: triggers: /etc|. The directories are processed in the order found in the variable. There are several special values which are interpreted: . It means `bk -R pwd`/BitKeeper/triggers is scanned for triggers. $BK_DOTBK If present, `bk dotbk`/BitKeeper/triggers is scanned for triggers. $BK_BIN If present, `bk bin`/BitKeeper/triggers is scanned for triggers. $NONE If present, with no other values, then no triggers are processed. $PRODUCT If present, `bk -P pwd`/BitKeeper/triggers is scanned for triggers. This only applies when in a component repository of a nested col- lection. It is a way to run product level triggers in each compo- nent. $SOMETHING_ELSE All other paths starting with "$" are ignored, that character is reserved. If the the variable is not defined, the default is: triggers: $PRODUCT|. TRIGGER CLASSES There are multiple event classes which may activate triggers: incoming events, outgoing events, deltas, commits, tags, undo, and collapses. The incoming event is broken into multiple events: incoming, resolve, and apply. Most events may have triggers which run before and/or after the event. The difference between pre- and post- event triggers is that pre-trig- gers may cause events to fail, but post-triggers are strictly informa- tional. The exit status from post-triggers are ignored. Not all triggers have both pre- and post- versions, the set of sup- ported triggers are as follows: pre-apply => called after the data has been merged in the RESYNC directory but before it is applied to the tree. Last chance to say no, allows examination of the merged changes. => called in the RESYNC directory, not the enclosing repository. => exit 0 allows the pull/push. => exit 1 fails the entire pull/push. => exit 2 fails the pull/push but leaves the patch in PENDING. => exit 3 fails the pull/push but leaves the patch in PENDING and the RESYNC tree in PENDING/RESYNC-date. pre-collapse => called before a changeset is collapsed (see bk collapse). Typical- ly used as part of a process to record the renaming of changesets in bug tracking systems. => exit 0 allows the collapse. => non-zero exit values will fail the collapse. pre-commit => called before a changeset is committed. => exit 0 allows the commit. => exit 1 fails the commit. If the commit was initiated from citool, citool will exit. See example below. => exit 2 also fails the commit. However, if it was initiated from citool, citool will not exit. This allows the user to try the com- mit again. See example below. post-commit => called after a changeset is committed or attempted to be committed. => typically used for notification. pre-delta => called before a delta is created. Can be use for triggers that check code style => exit 0 allows the delta. => exit 2 indicates that the trigger called delta itself, upon which the calling delta command treats it as a successful delta. => Other than 2, all other non-zero exit values will fail the delta. pre-incoming => called before an incoming push/pull is started. => non-zero exit status fails the incoming event. => typically used for locking down a repository. => may be used to fail the event based on remote user, host, directo- ry, and/or BitKeeper version. post-incoming => called after the data has been applied to the tree. => typically used for notification. pre-outgoing => called before an outgoing pull/push/clone event. => non-zero exit status fails the outgoing event. => typically used for locking down a repository. post-outgoing => called after the outgoing event. => typically used for notification. pre-resolve => called after the data has been union-ed in the RESYNC directory. => called in the RESYNC directory, not the enclosing repository. => exit 0 allows the pull/push. => exit 1 fails the entire pull/push. => exit 2 fails the entire pull/push but leaves the patch in PENDING. => typically used to examine changes before taking them. pre-tag => called before a tag event, such as a bk commit with a specified tag or a bk tag. => exit 0 allows the tag. => exit 1 causes the tag operation to fail as well as the commit oper- ation if the tag was part of a commit. => exit 2 causes the tag operation to fail but allows the commit oper- ation to proceed if the tag was part of a commit. pre-undo => called before a undo is run by user or by unpull and clone -r. => exit 0 allows the undo. => exit 1 causes the undo operation to fail. post-undo => called after an undo is run successfully. TRIGGER LOCKING Triggers are called with a locked repository. When a post trigger is called, the repository is read locked, even in the case that the event was an event which changed the repository. A read lock will allow the trigger to do outgoing events, such as a push, but will prevent incom- ing events. Pre-incoming and pre-commit triggers will be called with a write locked repository. TRIGGER SECURITY ISSUES Triggers are arbitrary programs (or scripts) which are run automatical- ly and without warning. It is possible that a malicious person could add a trigger which effect a security breach, damage files, etc. If you are operating in a non-trusted environment, you may disable all triggers by setting the BK_NO_TRIGGERS environment variable. This is the safest thing to do but then the trigger functionality is lost. Alternatively, a "paranoid" trigger could be added which refused to ac- cept a new trigger into a repository without being examined first. Here's an example of such a trigger, which would typically be named BitKeeper/triggers/pre-apply.paranoid: #!/bin/sh # This is running in the RESYNC tree, we're looking # for any new triggers and/or changes to triggers. # Done after the resolve stage because they could # be sneaky and create the file in an earlier # changeset and then move it. test `bk gfiles BitKeeper/triggers | wc -l` -gt 0 || exit 0 if [ $BK_SIDE = server ] then echo Refusing to accept any changes to triggers on push, echo get the project admin to pull your changes. exit 1 fi rm -f BitKeeper/tmp/t_reject for i in `bk gfiles BitKeeper/triggers` do ( echo Please review the following trigger for security risks. echo Do not accept it if you think it is a problem. echo echo ===== $i ===== bk cat $i ) > BitKeeper/tmp/prompt$$ bk prompt -fBitKeeper/tmp/prompt$$ \ -t"Review trigger" -yAccept -nReject STATUS=$? rm -f BitKeeper/tmp/prompt$$ test $STATUS = 0 || { touch BitKeeper/tmp/t_reject break } done test -f BitKeeper/tmp/t_reject && { rm -f BitKeeper/tmp/t_reject exit 3 } rm -f BitKeeper/tmp/t_reject exit 0 TRIGGER ENVIRONMENT VARIABLES Information which might be useful to the trigger is passed in environ- ment variables. There are variables for user, host, location, BitKeep- er version, repository level, amongst others. There are two classes of variables, client side variables (BK_*) and server side variables (BKD_*). The client side variables are associated with the user who initiated the command. The server side variables, if present, are as- sociated with the "other" repository. For example, if a user on host "to" does a pull from host "from", then BK_HOST=to and BKD_HOST=from. In the list of variables which follow, BKD_* variables are not present unless the command has two end points, such as a pull, push, or clone. The BKD_* variables are not defined for commit, resolve, and apply events. In all other cases, the variable is present in all triggers unless otherwise stated. BK_CSETLIST If set, contains the name of a file which contains the list of changesets being received. Valid in pre-apply, post-incoming pre-outgoing, post-outgoing, pre-resolve, and pre-undo triggers. BK_CSETS If set, contains the list of changesets being sent. Valid only in pre-outgoing and post-outgoing clone events. BK_COMMENTFILE Location of the comment file for the changeset. Useful when writing triggers that need to parse the changeset comments. See example below. BK_EVENT The event from the point of view of the trigger. The full list of values for this variable is: apply, col- lapse, commit, delta, fix, incoming clone, incoming port, incoming pull, incoming push, outgoing clone, out- going pull, outgoing push, resolve, tag, and undo. BK_FILE Valid only in the pre-delta trigger, and contains the filename of the file about to be delta-ed, relative to the repository root. BK_HOST The hostname of the client side host. BKD_HOST The hostname of the server side host. BK_LEVEL The "level" of the client side repository. BKD_LEVEL The "level" of the server side repository. BK_LOCALCSETS The number of changesets (and/or tags) which are not present in the remote repository but are present in the local repository. Note that this variable does not have a BKD_ version because it is valid only on the outgoing end of a pull or a push. The other variable is BK_RE- MOTECSETS. Both variables are valid in pre-outgoing and post-outgoing triggers only. BK_REPO_TYPE The repository type. One of product, component, or standalone. BKD_REPO_TYPE As above. BK_PATCH Valid only in the pre-resolve trigger, and contains the full pathname of the file containing the patch being re- solved. BK_PENDING Contains the name of a file which contains the list of files with pending deltas. Valid only in pre-commit. BK_REMOTECSETS The number of remote changesets (and/or tags) which are not present in the local repository but are present in the remote repository. Goes with BK_LOCALCSETS and is valid only in pre-outgoing and post-outgoing triggers. BK_ROOT The full path name to the root of the client side repos- itory. BKD_ROOT The full path name to the root of the server side repos- itory. BK_SIDE If the trigger is part of a two-sided operation (i.e., pull, push), then this is set to "server" if the trigger is running on the server repository. Otherwise this is set to "client." BK_STATUS The status of the command, if known. Values may in- clude: NOTHING There was nothing to pull or push. FAILED The command did not complete because of an error. DRYRUN The command did not complete because it was a "dry run," i.e., a "bk pull -n" to look to see if there is anything to pull. CONFLICTS The command did not complete because there were con- flicts (parallel work). LOCAL_WORK The command did not complete because there is local work and an update only operation was requested. SIGNALED The command did not complete because it received a sig- nal. OK The command completed successfully. UNKNOWN Unknown status. BK_TAG If set, contains the value of the symbolic tag to be added to the repository. Valid only in pre-tag trigger. BK_TAG_REV If set, contains the changeset revision on which the tag will be placed. This will be set if and only if the re- vision is not the most recent revision. Valid only in pre-tag trigger. BK_TIME_T The UNIX style time stamp of the client side BitKeeper binary. BKD_TIME_T The UNIX style time stamp of the server side BitKeeper binary. BK_TRIGGER The basename name of the trigger program. BK_USER The user name of the user who ran the command on the client. BKD_USER The user name of the user who ran the command on the server. BK_UTC The time stamp of the client side BitKeeper binary ex- pressed as YYYYMMDDHHMMSS. BKD_UTC The time stamp of the server side BitKeeper binary ex- pressed as YYYYMMDDHHMMSS. BK_VERSION The version of the client side BitKeeper binary as the symbolic name or the UTC. BKD_VERSION The version of the server side BitKeeper binary as the symbolic name or the UTC. EXAMPLE 1 #!/bin/sh # Simple post-commit trigger for email notification of changes # For nested collections, we don't want notification in components, # the product recurses. test `bk repotype` = "component" && exit 0 # Let bk changes do all the work for us. bk changes -vvr+ | mail -s "commit in `bk gethost -r`:`bk pwd` bk $BK_USER" exit 0 EXAMPLE 2 #!/bin/sh # Display info about incoming and outgoing csets. if [ X$BK_STATUS = XDRYRUN -o X$BK_STATUS = XNOTHING ] then exit 0 fi if [ $BK_SIDE = server ] then U=$BKD_USER H=$BKD_HOST R=$BKD_ROOT else U=$BK_USER H=$BK_HOST R=$BK_ROOT fi ( if [ X$BKD_ROOT != X ] then printf '%-10s%-20s%-20s\n' VAR CLIENT SERVER printf '%-10s%-20s%-20s\n' === ====== ====== printf '%-10s%-20s%-20s\n' USER $BK_USER $BKD_USER printf '%-10s%-20s%-20s\n' HOST $BK_HOST $BKD_HOST printf '%-10s%-20s%-20s\n' ROOT $BK_ROOT $BKD_ROOT printf '%-10s%-20s%-20s\n' LEVEL $BK_LEVEL $BKD_LEVEL printf '%-10s%-20s%-20s\n' TIME_T $BK_TIME_T $BKD_TIME_T printf '%-10s%-20s%-20s\n' UTC $BK_UTC $BKD_UTC printf '%-10s%-20s%-20s\n' VERSION $BK_VERSION $BKD_VERSION echo fi echo ${U}@${H} fired the $BK_TRIGGER trigger in $R case $BK_TRIGGER in pre-outgoing) VERB=Sending;; post-outgoing) VERB=Sent;; pre-incoming) VERB=Receiving;; post-incoming) VERB=Received;; pre-resolve) VERB=Resolving;; pre-commit) VERB=Committing;; post-commit) VERB=Committed;; pre-apply) VERB=Applying;; esac if [ X$BK_PENDING != X ] then ( echo $VERB the following deltas echo bk log - < $BK_PENDING ) | sed 's/^/ /' fi if [ X$BK_CSETLIST != X ] then ( echo $VERB the following changesets echo bk changes -v - < $BK_CSETLIST ) | sed 's/^/ /' fi if [ X$BK_CSETS != X ] then ( echo $VERB the following changesets echo bk changes -v -r$BK_CSETS ) | sed 's/^/ /' fi ) | mail -s "$BK_EVENT in ${H}:${R}" notify@bitkeeper.com EXAMPLE 3 #!/bin/sh # # Using pre-commit trigger to verify changeset comment # BitKeeper/trigger/pre-commit.cset_comments # # only run in the product (or a traditional repo) bk repotype -q test $? -eq 1 && exit 0 # if this is a merge, we are in the RESYNC directory # since it is not a user cset, ignore test "`basename "$BK_ROOT"`" = "RESYNC" && exit 0 grep -q 'BUGID:' $BK_COMMENTFILE && exit 0 msg="A 'BUGID:' field is needed in the checkin comments" # Bring up an external program to browse bugs so that the # engineer can add a valid bugid to the changeset comments. #/opt/bugtrack/bin/bugviewer 'http://server.host.com/bugs?status=open' & # Ask user if he needs to enter a bugid. returning an exit code of 2 # from the trigger will allow user to retry the commit from # within citool. bk prompt -y"Reenter bugid" -n"Force commit with no bugid" "$msg" && exit 2 # If you wish to provide the option to fail out of citool, use this. #bk prompt -y"Abort commit" -n"Force commit with no bugid" "$msg" && exit 1 exit 0 EXAMPLE 4 #!/bin/sh # # Using a pre-collapse trigger to checks to see if the key is in # some repo that is frozen. bk changes -R bk://work/bk-4.0.x - < $BK_CSETS > /tmp/csets$$ OK=0 test -s /tmp/csets$$ && OK=1 rm -f /tmp/csets$$ exit $OK EXAMPLE 5 #!/bin/sh # # A post-incoming trigger that will push incoming changesets # to a mirror repository MIRROR=REPO-mirror # Debug #Q= #OUT=/tmp/OUT # Quiet Q=-q OUT=/dev/null # Do not run in components test `bk repotype` = component && exit 0 # Only run on successful pulls/pushes test "$BK_EVENT" = "incoming clone" && exit 0 test "$BK_STATUS" != "OK" && exit 0 # Foreground mirror - incoming operation will block # until the mirror push is complete #bk push $Q "$MIRROR" #exit 0 # Background mirror # Start a background process to push the incoming # changesets to a mirror repository # ( # Avoid possible race, wait for any locks to be dropped bk lock -U bk push $Q "$MIRROR" ) > $OUT 2>&1 & SEE ALSO bk clone bk changes bk commit bk collapse bk delta bk prompt bk pull bk push bk re-solve bk repotype bk tag CATEGORY Repository BitKeeper Inc 1E1 bk triggers(7.3ce)