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 directory 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 trigger 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 manager 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/triggers/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
BitKeeper/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
separated 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 repositories’ BitKeeper/triggers,
set the variable as follows in your configuration:
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 collection. It is a way to run product level triggers in each component.
$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-triggers may cause events to fail, but post-triggers are strictly informational. The exit status from post-triggers are ignored.
Not all triggers
have both pre- and post- versions, the set of supported
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). Typically 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 commit 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, directory, 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 operation if the tag was part of a commit.
☞ exit 2 causes the tag operation to fail but allows
the commit operation 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 incoming
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
automatically 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 accept 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 environment variables. There are variables for user,
host, location, BitKeeper 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
associated 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, collapse, commit, delta, fix, incoming clone,
incoming port, incoming pull, incoming push, outgoing clone,
outgoing 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_REMOTECSETS .
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 resolved.
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 repository.
BKD_ROOT The full path name to the root of the
server side repository.
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 include:
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
conflicts (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
signal.
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 revision 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 expressed as YYYYMMDDHHMMSS.
BKD_UTC The time stamp of the server side
BitKeeper binary expressed 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
|
VERB=Sending;; |
||||||
|
VERB=Sent;; |
||||||
|
VERB=Receiving;; |
||||||
|
VERB=Received;; |
||||||
|
VERB=Resolving;; |
||||||
|
VERB=Committing;; |
||||||
|
VERB=Committed;; |
||||||
|
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}"
[email protected]
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-resolve,
bk-repotype, bk-tag
CATEGORY
Repository