Hello committers / release managers!
You have often experienced the tar ball release scenario,
when the RM tries to get the stable tree out as a release
candidate, but then someone commits in the last few minutes!
Now imagine the following scenario:
% cvs commit -m "Commit one final patch before RM rolls the tarball"
==============================================================
ERROR: The apache-1.3 CVS tree is locked for maintenance!
Commits/Tags are currently only allowed for users:
jim
In urgent cases, ask one of these committers to release the lock.
==============================================================
cvs server: Pre-commit check failed
cvs [server aborted]: correct above errors first!
Exit 1
or with a TAG which is about to be applied:
% cvs tag foobar
==============================================================
ERROR: The apache-1.3 CVS tree is locked for maintenance!
Commits/Tags are currently only allowed for users:
jim
gstein
In urgent cases, ask one of these committers to release the lock.
==============================================================
cvs server: Pre-tag check failed
cvs [server aborted]: correct the above errors first!
Exit 1
Instead of "apache-1.3", you can lock any repository, and instead
of "jim" or "gstein", you can have any RM's user id.
(In fact, every committer could set a lock...)
The script which is attached solves (for me, at least) the fear that
unwanted commits or taggings can influence the release. Simply set a
lock when you freeze the tree, and remove the lock after the release.
For the release manager, it looks like this:
% cvs checkout apache-1.3 # pull whole tree
% cd apache-1.3 # and inside, create a .frozen file
% echo ${USER} >.frozen # with the release manager's id.
% cvs add .frozen # we assume no lock is there yet.
The critical region starts here:
% cvs commit -m "Lock" .frozen # from now on, we have control.
... edit httpd.h, commit, tag, roll etc...
% cvs rm -f .frozen # Finally, delete the .frozen file
The critical region ends after the following commit:
% cvs commit -m "Unlock" .frozen # and allow free commits again
If, during the "critical region", another committer must be
granted access, you simply add her to the .frozen file:
% echo sally >>.frozen # sally may commit as well.
% cvs commit -m "Add Sally" .frozen
You can never lock yourself out: the script checks this.
Of course, enough karma to install the script is required (once),
but after that, it sits in the background and does nothing, unless
a lock file (.frozen or whatever) appears at the base of the tree
which is under control of the script. And cvs allows flexible control
over the repositories which should be checked for locks.
The administrator only needs to do the following to activate it (once):
% cvs checkout CVSROOT # Get cvs administrative files
% cd CVSROOT
% cp ...../check-frozen . # Add this script.
% chmod 755 check-frozen # Make script executable
% cvs add check-frozen # Add to CVSROOT repository
% echo check-frozen >>checkoutlist # Add to administrative files
% echo '^apache-1.3/? /home/cvs/CVSROOT/check-frozen commitinfo' >>commitinfo
% echo '^apache-1.3/? /home/cvs/CVSROOT/check-frozen taginfo' >>taginfo
Replace "apache-1.3" by your cvs repository name in the lines above,
and invoke an editor instead of adding the line with "echo >>".
Finally, commit all changed files to enable lock ckecking:
% cvs commit -m "Add .frozen lock support to apache-1.3" \
check-frozen checkoutlist commitinfo taginfo
That's it. Try it on your own CVS roots at home, and tell me whether
we should use it on apache.org
I checked it both with :pserver: and /home/cvs (local) repositories.
I expect :ext: to be exactly like local.
Martin
--
<[EMAIL PROTECTED]> | Fujitsu Siemens
<[EMAIL PROTECTED]> | 81730 Munich, Germany
#!/bin/sh
LOCK=.frozen
DEBUG=false
# check-frozen: $Id: check-frozen,v 1.36 2001/02/27 20:25:52 martin Exp $
#
# Initially written by Martin Kraemer <[EMAIL PROTECTED]> Feb/2001
#
# This shell script allows "freezing" of a cvs tree for release management.
# It is used like this:
# % cvs checkout apache-1.3 # pull whole tree
# % cd apache-1.3 # and inside, create a .frozen file
# % echo ${USER} >.frozen # with the release manager's id.
# % cvs add .frozen # we assume no lock is there yet.
# The critical region starts here:
# % cvs commit -m "Lock" .frozen # from now on, we have control.
#
# ... edit httpd.h, commit, tag, roll etc...
#
# % cvs rm -f .frozen # Finally, delete the .frozen file
# The critical region ends after the following commit:
# % cvs commit -m "Unlock" .frozen # and allow free commits again
#
# If, during the "critical region", another committer must be
# granted access, you simply add her to the .frozen file:
# % echo sally >>.frozen # sally may commit as well.
# % cvs commit -m "Add Sally" .frozen
# You can never lock yourself out: the script checks this.
#
# BUGS:
# The script uses the rcs command 'co -p' (instead of the
# "cvs update -p" which would be more appropriate but
# which only works in checked out working copies) because it
# accesses the cvs/rcs files directly. There is not enough
# information in the cvs server (pserver/ext/local) process to
# use a cvs command to extract the current file contents.
#
# Administrative background:
#
# We are using the taginfo and commitinfo hooks to control
# who is allowed to do what. The script itself is added to
# CVSROOT and added as an "administrative file":
#
# % cd ~/.../ # Somewhere (near $HOME),...
# % cvs checkout CVSROOT # Get cvs administrative files
# % cd CVSROOT
# % cp ...../check-frozen . # Add this script.
# % chmod 755 check-frozen # Make script executable
# % cvs add check-frozen # Add to CVSROOT repository
# % echo check-frozen >>checkoutlist # Add to administrative files
# % echo '^apache-1.3/? /home/cvs/CVSROOT/check-frozen commitinfo' >>commitinfo
# % echo '^apache-1.3/? /home/cvs/CVSROOT/check-frozen taginfo' >>taginfo
#
# Replace "apache-1.3" by your cvs repository name in the lines above,
# and invoke an editor instead of adding the line with "echo >>".
# Finally, commit all changed files to enable lock ckecking:
#
# % cvs commit -m "Add .frozen lock support to apache-1.3" \
# check-frozen checkoutlist commitinfo taginfo
#
# ==============================================================
# During invocation, we get the following information from CVS:
#
# $1 is the invocation tag from the commitinfo/taginfo file
# (see above; it helps us decide what command we were called for).
#
# If $1 is "commitinfo" (we are in a commit transaction):
# $2 = the base directory (/home/cvs/name) of the current commit
# $3...n the list of files to be committed, relative to `pwd`
#
# If $1 is "taginfo" (we are in a tag transaction):
# $2 -- tagname
# $3 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
# $4 -- repository
# $5 $6 [$n $n+1 ...] -- file revision [file revision ...]
#
# Also, cvs provides us with the following environment variables
# (even for remote accesses using the :pserver: method):
# $CVS_PID the pid of the cvs server which does the commit
# $CVSROOT base directory of the cvs tree
# $USER user name of committer
# $LOGNAME user name of committer
# `pwd` = the temporary directory on the server where the files
# have been set up which are about to be committed
# (usually /tmp/cvs-serv$CVS_PID or the committer's PWD)
# ===============================================================
if $DEBUG
then
# Dump invocation args, nondestructively ($* is not modofied!):
i=0
while [ $i -le $# ]; do
case $i in
0) echo "arg[0]=$0";;
1) echo "arg[1]=$1";;
*) ( shift `expr $i - 1`; echo "arg[$i]=$1" );;
esac
i=`expr $i + 1`
done
# Print some environment:
echo pwd: `pwd`; ls -laR; env
echo '$$='$$ \$\CVS_PID=$CVS_PID; ps axl | egrep "$$|$CVS_PID"
fi
# ==============================================================
# Decide for which cvs transaction type we were called:
# ==============================================================
case ${1} in
taginfo|tag)
BASEDIR=${4}
OP=tag
;;
commitinfo|commit|ci)
BASEDIR=${2}
OP=commit
;;
*)
echo "ERROR: Misconfiguration: 1st arg must be 'taginfo' or 'commitinfo'"
exit 2
;;
esac
shift
# ==============================================================
# We are now looking for an (already committed) ${LOCK} file.
# The lock file might be...
# a) in the base directory of the project (as a CVS file with trailing ","), or
# b) in the Attic/ subdirectory if it was "cvs rm"ed earlier, or
# c) not there at all.
# If it exists, check the list of users against the committer's name:
# ==============================================================
[ ! -f "${BASEDIR}/${LOCK},v" ] || co -q -p "${BASEDIR}/${LOCK},v" | grep
"^${LOGNAME}\$" >/dev/null
ex=$?
case $ex in
0) ;;
*)
echo ==============================================================
echo "ERROR: The `expr ${BASEDIR} : "^${CVSROOT}"/'\(.*\)'` CVS tree is locked for
maintenance!"
echo ""
echo " Commits/Tags are currently only allowed for users:"
co -q -p "${BASEDIR}/${LOCK},v" | sed 's|^| |g'
echo ""
echo "In urgent cases, ask one of these committers to release the lock."
echo ==============================================================
;;
esac
# ==============================================================
# Check whether an updated ${LOCK} file is being committed.
# If it is, the committer MUST be listed in it, or she
# would lock herself out!
# ==============================================================
if [ "${OP}" = "commit" ]; then
shift # $1 is the cvs basedir. The rest is filenames
for f
do
case $f in
${LOCK})
# If the file is not there, then this is the transaction where
# a "cvs rm .frozen; cvs commit .frozen" is committed. Skip the check:
[ -f $f ] || continue
# Otherwise, the committer's name must be present:
if grep "^${LOGNAME}\$" ${LOCK} >/dev/null
then
:
else
echo ==============================================================
echo "ERROR: You are about to commit a ${LOCK} file without your"
echo " user name '${LOGNAME}' in it."
echo " If you would commit it, you would lock yourself out."
echo ""
echo " To add your (missing) name, do this:"
echo " % echo \"${LOGNAME}\" >>${LOCK}"
echo " % cvs commit `shift; echo $*`"
echo ""
echo " To release a lock, use this command sequence instead:"
echo " % cvs rm -f ${LOCK}"
echo " % cvs commit -m \"Release the lock\" ${LOCK}"
echo ==============================================================
ex=1
fi
esac
done
fi
# Finally, exit with 0 (okay, proceed) or 1 (don't commit/tag):
exit $ex