Tuesday, June 11, 2013

IBM Rational ClearCase: The ten best triggers

http://www.ibm.com/developerworks/rational/library/4311.html

IBM® Rational® ClearCase® is a Software Configuration Management tool that benefits both small and large software development teams.
The complexity of the tool, and the fact that individual developers use it in different ways, can make the life of a Software Configuration Manager quite difficult.
Over the last few years I've performed many implementations and presentations with ClearCase. I've observed that even though every project is unique, and each organization has its own approach to solving problems, the same questions about ClearCase usage always come up.
Since I didn't want to reinvent the wheel for every project, I collected some of the most useful triggers that I've used during implementations. I made these as generic as possible to increase their applicability across projects.
The practice has helped me immensely: I now have a collection of triggers that I can easily use to answer recurring questions about ClearCase usage.
This document describes the 10 most useful triggers, which should help make the daily work of a Software Configuration Manager less complicated.
A word of caution: All the triggers described in this document are delivered "as is." There is no official support offered by IBM for these scripts and triggers.
Also, as described in the ClearCase manual, triggers are always launched on the client computer, using the identity of the user who initiated the ClearCase command.
All my triggers were developed in one of three languages: either directly as shell / batch, in Perl, or using the ClearCase API (CAL).
I am not a Perl expert, and you will immediately see that my Perl language knowledge is limited, but hopefully these scripts are easy to understand. Nor am I a Visual Basic expert, and after looking over my VB programs you may get the feeling that they could be rewritten in a more optimized way.
In any case, feel free to contact me with either your comments or suggestions for improvements that you would like to see in these scripts.
Here's a list of the 10 best triggers for use with ClearCase, compiled from my daily work as a Rational consultant. I have categorized them as recommended in Christian Buckley and Darren Pulsipher's article, Trigger happy: Overcoming entropy in software engineering.
To use these triggers, you must be the VOB owner, and reside in the VOB upon which you wish to perform the command. Associated with each trigger, you'll find a command to attach it to a VOB.
Trigger scripts should be saved in a central, shared directory, such as NFS share for UNIX or SMB share for Microsoft Windows. The best central place to put ClearCase triggers and scripts is actually the VOB storage share. If that's not available to you, you probably don't have access to the VOBs, and so you probably don't need triggers.
In large organizations, ClearCase triggers and scripts can also be put under version control, and then accessed using a path such as:
  • /view/administration_view/VOB/trigger_VOB/directory (UNIX)
  • \\view\administration_view\trigger_VOB\directory (Windows)
Nevertheless, you must have an administration_view available on each client computer.
For example, all the triggers that follow, were stored in:
  • /net/titeuf/triggers/ (UNIX)
  • \\mw-ddiebolt\triggers\ (Windows)
ClearCase contains some very powerful commands, and you may want to limit access to them in a particular ClearCase installation. The following trigger shows how to prevent some project users from initiating a specific ClearCase command.
The cleartool rmelem command is one of the most powerful commands in ClearCase because it removes a complete element from version control. This command destroys information irretrievably. Using it carelessly may compromise your organization's ability to support previous releases.
Since this command can be so destructive, the cleartool rmname command should be used in its place in almost all cases.
It's probably pretty clear why this is one of the first commands on which I like to put a trigger.
Here is the trigger definition:
cleartool mktrtype -c "Trigger to avoid rmelem command" -element -all -preop rmelem -execwin "ccperl -e \"exit 1\"" -execunix "Perl -e \"exit 1\"" NO_RMELEM
The trigger script is pretty straightforward: it simply invokes the ClearCase embedded Perl interpreter and exits with a status offalse, so the ClearCase command rmelem cannot be executed.
When a user tries to execute the rmelem command, the following message appears:
Z:\rdn>cleartool rmelem test.txt
CAUTION! This will destroy the element, all its branches and versions,
including all data, meta-data and history, and will remove the element
from all directory versions that now contain it.  Once you destroy the
element, there will be no way to restore it to its current state.
If you want to preserve the element, but remove references to it from
future directory versions, use the "rmname" command.

Element "test.txt" has 1 branches, 4 versions, and is entered
in 1 directory versions.
Destroy element?  \\\\\[no\\\\\] yes
cleartool: Warning: Trigger "NO_RMELEM" has refused to let rmelem proceed.
cleartool: Error: Unable to remove element "test.txt".

Z:\rdn>

Post triggers perform an action after a ClearCase operation, to ensure that the action occurs in conjunction with the operation. For instance, you may want an e-mail notification sent after check-in or check-out. Following is a list of triggers that perform common post-operation actions.
\\\\\[Many thanks to Rob Platt for the nice error message.\\\\\]
Developers don't usually add comments when checking in or checking out a file: they can be quite lazy about this and, thus, prefer just to hit return instead of inserting an adequate comment explaining why they are making changes to the project artifacts.
Encouraging this good habit by verifying that a comment has been entered in a ClearCase operation is quite easy: ClearCase has a trigger environment variable, CLEARCASE_COMMENT, which contains the comment string.
This is the command to install the trigger script:
cleartool mktrtype -c "Trigger to check comment" -element -all -postop checkin -execwin "ccperl \\mw-ddiebolt\triggers\check_comment.bat" -execunix "Perl /net/titeuf/triggers/check_comment.pl" CHECK_COMMENT
And here the trigger script:
@rem = ' PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

$CLEARCASE_COMMENT      = $ENV{CLEARCASE_COMMENT};
$CLEARCASE_FILE1        = $ENV{CLEARCASE_PN};
$CLEARCASE_FILE2        = $ENV{CLEARCASE_XPN};

if ( $CLEARCASE_COMMENT eq "" )
        {
        $text = "Caution : We kindly ask you to put comments in the future...";

        $status = system("clearprompt proceed -prompt \"$text\" -prefer_gui");
        }

__END__
:endofperl

In the script, we check if comments were added. If not, we invoke "clearprompt," which displays a nice message to the user.
Developers tend to have complex configuration specifications with a lot of branches. They tend to check out files just in case they need to modify them, and then after consideration they decide to reverse the checkout.
This results in elements with branches that are empty, that is, they only contain version 0.
Empty branches cause neither problems nor performance issues, but it is neater to remove them.
Here is a trigger that can remove the unnecessary empty branch on the fly.
Installation of the trigger:
cleartool mktrtype -c "Automatically remove empty branch" -element -all -postop uncheckout -execwin "ccperl \\mw-ddiebolt\triggers\test_empty_branch.bat" REMOVE_EMPTY_BRANCH
Trigger script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

###########################################
# Begin of Perl section

use strict;

my $idstring = $ENV{CLEARCASE_ID_STR};
my $element  = $ENV{CLEARCASE_XPN};
my $count = 0;
my $nidstring = $idstring;
my $string;

#printf "\n Trigger is fired $idstring...\n";
$nidstring =~ s/\\0/XXXXXXXXXX/g;
$count = ($nidstring =~ tr/X//);
#printf ("\n $nidstring Count : $count \n");

if ($count == 10)
        {
        # printf "\n Only version 0 \n";
        $nidstring = $idstring;
        $nidstring =~ s/\\main\\0/XXXXXXXXXX/g;
        $count = ($nidstring =~ tr/X//);
        # printf ("\n $nidstring Count : $count \n");
        if ($count != 10)
                {
                $element =~ s/\\0//g;
                $string = "cleartool rmbranch -force $element";
                system($string);
                #printf "\n ToDo : $string \n";
                }
        else
                {
                # We cannot delete branch main !!!
                }
        }
else
        {
        # There is not only version 0 !!!
        }

# End of Perl section

__END__
:endofperl

The script automatically removes the branch when there is only a version 0 on it.
Caution: If you have put a label on version 0, or on the checked-out version where you undo the checkout, the branch will still be deleted.
Here is an example of usage:
Z:\rdn>cleartool mkbranch test_branch test.txt
Creation comments for "test.txt@@\main\test_branch":
.
Created branch "test_branch" from "test.txt" version "\main\1".
cleartool: Warning: Version checked out ("\main\test_branch\0") is different from 
version selected by view before checkout ("\main\1
").
Checked out "test.txt" from version "\main\test_branch\0".

Z:\rdn>cleartool unco test.txt
Save private copy of "test.txt"?  \\\\\[yes\\\\\] no
Removed branch "Z:\rdn\test.txt@@\main\test_branch".
Checkout cancelled for "test.txt".

\\\\\[Many thanks to SwissLife / Urs Winkenbach, who told me about this very good trigger.\\\\\]
The person who puts a file under version control is usually its owner. This element owner has special permission over it. He can remove the element, remove the version, and so on.
It is a better practice if all the elements belong to the same owner -- the VOB owner. Since the VOB owner is usually the ClearCase Administrator, this also results in normal users being restricted from a lot of permissions to which they perhaps don't need access.
Script to create the trigger:
cleartool mktrtype -c "Trigger to change element ownership"
 -element -all -postop mkelem -execwin
 "ccperl \\mw-ddiebolt\triggers\change_owner.bat"  CHANGE_OWNER

Trigger script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

$COMPUTERNAME           = $ENV{COMPUTERNAME};

$VOB            = $ENV{CLEARCASE_VOB_PN};
$filename       = $ENV{CLEARCASE_PN};

$owner = `cleartool describe -long VOB:$VOB | findstr owner | findstr \/v ownership`;
$owner =~ s/owner //g;
$owner =~ s/ //g;
chop $owner;

if (!($owner eq ""))
        {
        $status = system("cleartool protect -chown $owner \"$filename\"");
        exit $status;
        }
fi

# End of Perl section

__END__
:endofperl

When a new file is put under version control (either over the GUI or over the command line) with a cleartool mkelem command, this script automatically sets the owner as the VOB owner.
\\\\\[Many thanks to Daniel Mencnarowski\\\\\]
Elements under version control in ClearCase have bits permission, in the POSIX form. For example, each file has a set of permissions in the form rwxrwxrwx for the user, group, and others.
Therefore the "execute" bit, a special bit, needs to be set if you want to be able to run the element.
In Windows, this means that .exe, .com, .bat, .dll, and .cmd elements need to have this execute bit set.
A trigger, over mkelem, can easily be set to perform this task.
This is the command to install the trigger:
cleartool mktrtype -c "Put execute bits for elements" -element -all -postop mkelem -execwin "ccperl \\mw-ddiebolt\triggers\executebit.bat" EXECUTEBIT
And this is the script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

###########################################
# Begin of Perl section

$file = $ENV{CLEARCASE_PN};
$lowerfile = lc($file);
#printf("executebit.bat : $lowerfile...\n");

if (($lowerfile =~ /.exe$/) ||          # Executable
    ($lowerfile =~ /.com$/) ||          # COM files
    ($lowerfile =~ /.cmd$/) ||          # Old dos CMD files
    ($lowerfile =~ /.bat$/) ||          # Batch files
    ($lowerfile =~ /.dll$/))            # Dll's need the bit also
{
#       printf("Putting execute bits...\n");
        $status = system("cleartool protect -chmod +x \"$file\"");
}
# End of Perl section
__END__
:endofperl

The trigger is fired after new elements are created, and it changes the protection only if the element has an .exe, .com, .cmd, .bat, or .dll extension.
Here is an example:
Z:\rdn>cleartool mkelem test.bat
Creation comments for "test.bat":
This is a test
.
Changed protection on "\rdn\.\test.bat".
Created element "test.bat" (type "text_file").
Checked out "test.bat" from version "\main\0".

Z:\rdn>cleartool des test.bat@@
file element "test.bat@@"
  created 03-juil.-02.10:53:29 by ddiebolt.staff@mw-ddiebolt
  "This is a test"
  element type: text_file
  Protection:
    User : SWITZERLAND\ddiebolt : r-x
    Group: SWITZERLAND\staff : r-x
    Other:          : r-x
  source pool: sdft  cleartext pool: cdft

Z:\rdn>

It was quite late one Wednesday evening and, after a day of implementing and describing ClearCase, we were all feeling tired.
As a joke, someone came to me and asked if ClearCase could also make coffee.
I answered that yes, something like that could be done -- when checking out a file, ClearCase can send a message to the secretary, asking for a coffee delivery.
Command to create the trigger:
cleartool mktrtype -c "Get some coffee trigger" -element -all -postop checkout -execwin "ccperl \\mw-ddiebolt\triggers\netsend.bat" COFFEE
Trigger script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

###########################################
# Begin of Perl section


#
# Configuration Variables
#

#use strict;


if ($ENV{TEMP})
        {
        $TDIR=$ENV{TEMP};
        }
else
        {
        $TDIR="c:${S}temp";
        }

$S                      = "\\";
$JUNK                   = $TDIR . $S . "junk";

$coffee_choosed         = "c:".$S."temp".$S."coffee_choosed";
$LIST_COMPUTER          = "\\\\mw-ddiebolt\\triggers\\list_computers.txt";


$CLEARCASE_XPN          = $ENV{CLEARCASE_XPN};
$CLEARCASE_USER         = $ENV{CLEARCASE_USER};
$CLEARCASE_COMMENT      = $ENV{CLEARCASE_COMMENT};




$status = system("clearprompt list -outfile $coffee_choosed -items \"dark
coffee\",\"tea\",\"chocolate\" -prompt \"Choose a coffee\"
 -prefer_gui");
if ($status != 0)
        {
        exit $status;
        }
$coffee  = `cmd /c type $coffee_choosed`;
chop $coffee;
$status = `del $coffee_choosed`;

$MESSAGE = "$CLEARCASE_USER : The $CLEARCASE_XPN file was checked out. Please bring 
$coffee !!!";

open(COMPUTER,$LIST_COMPUTER);
while ($computer=<COMPUTER>)
        {
        chop $computer;
        printf("Echo  Processing computer : $computer \n");
        $command="net send $computer \" $MESSAGE \" > NUL 2> NUL";
        $status = system($command);
        }
close(COMPUTER);


# End of Perl section

__END__
:endofperl

Example of a list_computer.txt:
mw-ddiebolt
During file checkout, you get the following message:
The menu diagram
Figure 1: The menu diagram.
After choosing your favorite beverage, your secretary will be sent the following message:
Messenger Service window
Figure 2: Just make sure you define an address other than mine when using this
Sometimes, we want to enforce a particular convention or style of usage for ClearCase. To do this we place a trigger that occurs before an operation, to make sure that all ClearCase users follow this convention. The following example makes sure that the names of newly created labels adhere to a particular convention.
See the downloads section for a downloadable version of this code.
\\\\\[Many thanks to Schindler Lift, Beat Arnold, who gave me these good ideas.\\\\\]
After a few months of using ClearCase and Software Configuration Management, you tend to get a lot of labels in the VOB database.
The naming consistency, of course, is not always the best. Many times I've seen variations of the same "conceptual" label: REL1, RELEASE_1, or REL_1.0.
A trigger to check label-naming convention is always a good idea: you force people to follow the same guidelines, and you also control the labeling of the project more efficiently.
This is the command to install the trigger:
cleartool mktrtype -c "Trigger to check label naming" -type -preop mktype -lbtype -all -execwin "ccperl \\mw-ddiebolt\triggers\test_label_name.bat" -execunix "Perl /net/titeuf/triggers/test_label_name.pl" CHECK_LABEL_NAME
And this is the script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

###########################################
# Begin of Perl section

#
# Configuration Variables
#

my $USERPREFIX = 'USER';
my $BUGPREFIX = 'BUG';
my $RELPREFIX = 'REL';

#######################################
#
# function definition
#

sub checkUserName {
  my $user = shift(@_);

  my $currentUser = uc($ENV{CLEARCASE_USER});

 if ($user !~ m/^($currentUser)/) {
    `clearprompt proceed -mask proceed -type error -prompt \"Can't create label f
    exit 1;
  }
  exit 0;
}


####################################################
#
# main section
#

{
  my $label = $ENV{CLEARCASE_LBTYPE};

  my $prefix;
  my $KAno;
  my $project;
  my $version;
  my $stream;



  # check, if label is in uppercase format


  if ($label ne uc($label)) {
    `clearprompt proceed -mask proceed -type error -prompt \"CCFS: Label not in u
    exit 1;
  }

  # decompose label
  if ($label =~ m/^($USERPREFIX)_(.*)_(.*)$/)
  {
    &checkUserName($2);

  } elsif ($label =~ m/^($BUGPREFIX)_(.*)$/)
  {
    exit 0;
  }
  elsif ($label =~ m/^($RELPREFIX)_(.*)$/)
  {
    exit 0;
  }
  {
    # no valid prefix found
    `clearprompt proceed -mask proceed -type error -prompt \"No valid Label speci
    exit 1;
  }

}

# End of Perl section

__END__
:endofperl

In the script, we verify that the label is correctly named: in uppercase format, and beginning with either USER, BUG, or REL.
Using this trigger, only the following forms can be created:
  • USER_<userlogin>_<something>
  • REL_<something>
  • BUG_<something>
If the new label does not follow the naming convention, then the following error message is displayed:
Error Message window
Figure 3: Now you have a gentle reminder to follow naming conventions
Another class of triggers, integration triggers, allows ClearCase to be integrated with other aspects of the underlying operating system, or with other tools that may be used in your software development environment. The following triggers integrate with the operating system and with other defect tracking systems.
UCM (Unified Change Management) implements a process over ClearCase that is mainly used in activity-oriented Software Configuration Management.
For example, you are asked to associate an activity with each change you perform on the project artifacts, to help describe why the change was performed.
Simulating that in normal ClearCase -- prompting the user to input an activity (like a bug or a feature reference) at each checkin -- isn't difficult with clearprompt.
Furthermore, you can store the information about the bug/feature number in an attribute.
Following is a minimal implementation of this concept.
You first need to define a new string attribute to store the bug/feature number.
cleartool mkattype -c "Attribute to store bug/feature number" BUGNUM
It should then look like:
Z:\rdn>cleartool des attype:BUGNUM
attribute type "BUGNUM"
  created 09-juil.-02.14:22:34 by ddiebolt.staff@mw-ddiebolt
  "Attribute to store bug/feature number"
  owner: SWITZERLAND\ddiebolt
  group: SWITZERLAND\staff
  scope: this VOB (ordinary type)
  value type: string
  

Command to create the trigger:
cleartool mktrtype -c "Trigger to store bug/feature number" -element -all -postop checkin -execwin "ccperl \\mw-ddiebolt\triggers\put_bug_num.bat" BUG_NUMBER
Trigger script:
@rem = ' PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

$CLEARCASE_FILE  = $ENV{CLEARCASE_XPN};

$outfile  = "c:\\bug_num.txt";


$text = "What is the bug / feature number reference ?";
$status = system("clearprompt text -outfile  $outfile -prompt \"$text\" 
-prefer_gui"); 
$bug_num = `cmd /c type $outfile`;

printf("\n Put bug num $bug_num on $CLEARCASE_FILE \n");

if ($status != 0)
 {
 exit $status
 }
else
 {
 system("cmd /c del $outfile");

 $status = system("cleartool mkattr -replace BUGNUM \"\\\"$bug_num\"\\\" 
$CLEARCASE_FILE ");

 exit $status;
 }

__END__
:endofperl

During element checkin, you see the following dialog message:
Prompt asking for bug / feature number reference
Figure 4: Here, you are prompted to enter reference information
If you then display the version tree of the checked-in file, you get:
ClearCase Version Tree Browser
Figure 5: Note the naming of the bug
\\\\\[Many thanks to David Weintraub.\\\\\]
Adding the version number, the history, and other information from the Software Configuration Management system directly to the source file is a common request.
Users of RCS SCM systems are used to being able to input the source file keywords during checkin/checkout.
A trigger at checkin can simulate this behavior in ClearCase.
Installation of the script:
cleartool mktrtype -c "RCS Keyword substitution in a source file" -element -all -preop checkin -eltype text_file -execwin "ccperl \\mw-ddiebolt\triggers\keyword_subst.perl" -execunix "Perl /net/titeuf/triggers/keyword_subst.perl" KEYWORD_SUBST
The keyword_subst.perl script:
package CCASETriggers::keyword_subst;
sub run {
########################################################################
# PURPOSE: CLEAR CASE CHECKIN TRIGGER
#
# original version programmed by: David Weintraub
# Date: February 4, 1997
# Description: This program uses the RCS strings and fills them
#              in. Certain strings can be marked as "required"
#              and checks will fail if they aren't found in the file.
#
########################################################################
# REQUIRE STRINGS
#
# A little backwards, but simple. If the value of the following is
# zero, the RCS string is required, and a checkin won't procede if
# not found.
#
$require{"Author"} = 1;         #Name of User who Checked in Program
$require{"Date"} = 1;           #Date & Time (GMT)
$require{"Header"} = 1;         #$directory/$CLEARCASE_XPN Date Author
$require{"Id"} = 1;             #$CLEARCASE_XPN Date Author
$require{"Name"} = 1;           #Program's Name
$require{"Revision"} = 1;       #branch/revision
$require{"Source"} = 1;         #$CLEARCASE_XPN
$require{"Log"} = 1;            #This is where Checkin Comments go!
my $logLength = @_\\\\\[0\\\\\];          #Minmum size required for comments.
$logLength = 0 unless ($logLength); #just in case it's not defined
########################################################################
# CONSTANTS
#

$VOB="$ENV{'CLEARCASE_VOB_PN'}";
$unix=($VOB =~ /^\/+.*/);
$Source = $Name = $CC_PN = "$ENV{CLEARCASE_PN}";
$Revision = "$ENV{CLEARCASE_ID_STR}";
$Log = $ENV{'CLEARCASE_COMMENT'};
$Author = $author = "$ENV{CLEARCASE_USER}";
$userName = "$Author";
if ($unix) {
   $userName = (getpwnam("$Author"))\\\\\[6\\\\\];
   $Author = "$userName ($Author)";
   $Name =~ s+.*/++;            #Remove Directory Information
} else {
   # remove Drive and View information;
   $view = "$ENV{CLEARCASE_VIEW_TAG}";
   $Source =~ s/^\w://;
   $Source =~ s/^.*\\($view)\\/\\/;
   $Name = $Source ;
   $Name =~  s+.*\\++;
}
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
$year = $year + 1900;  # not a Y2K bug, but functionality of fct. gmtime
$mon  = $mon  + 1;     # range of month:0..11
$Date = sprintf ("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d GMT", $year, $mon, $mday, $hour,
$min, $sec);
$Header = "${Source}\@\@$Revision  ${Date}  ${author}";
$Id = "${Name}\@\@${Revision}  ${Date}  ${author}";
$whatID="@(#)";
$tmp = $ENV{TMP};
$tmp = $ENV{TEMP} unless ($tmp);
$tmp = "/tmp" unless ($tmp);
$workFile = "$tmp\\ct.ci.$$";
########################################################################
# First: CHECK COMMENT LENGTH (just in case the trigger check_comment
#        might be the second one called)
#
if (length($Log) < $logLength) {
    $message =  "Your Checkin Comment must be at least $logLength character";
    $message .= " long. \nRedo the Checkin with a longer comment!!!\n";
    die("$message");
}
########################################################################
# CHECK IF IDENTICAL VERSION OF FILE
#
#chop ($diff = `cleartool diff -pre -ser  "$CC_PN"`);
#die("'$Name' is identical to it's predecessor. Use uncheckout instead...\n")
#    if ( $diff eq "Files are identical");
########################################################################
# OPEN WORK FILE AND SOURCE FILE
#
open("SOURCE", "$CC_PN") ||
    &error("Could not open Source File $CC_PN for reading");
open("WORKFILE", ">$workFile") ||
    &error("Could not open Work File $workFile for writing");
########################################################################
# LOOP THROUGH FILE AND FIND WHAT STRINGS
#
while (<SOURCE>)
{
    $LogLineState = 0;                          #Needed for below
#
#   ####Quite Simple, if you find the RCS Keyword in the file
#   ####Simply substitute in the new value. Then you can write
#   ####this line back to the work file.
#
    SWITCH: {
       /\$Author:?.*\$/  && do {
                    s/\$Author:?.*\$/\$Author: $Author \$/;
                    $require{"Author"} = 1;
                    last SWITCH;
       };
       /\$Date:?.*\$/ && do {
                    s/\$Date:?.*\$/\$Date: $Date \$/;
                    $require{"Date"} = 1;
                    last SWITCH;
       };
       /\$Header:?.*\$/ && do {
                    s/\$Header:?.*\$/\$Header: $whatID $Header \$/;
                    $require{"Header"} = 1;
                    last SWITCH;
       };
       /\$Id:?.*\$/ && do {
                    s/\$Id:?.*\$/\$Id: $whatID $Id \$/;
                    $require{"Id"} = 1;
                    last SWITCH;
       };
       /\$Name:?.*\$/ && do {
                    s/\$Name:?.*\$/\$Name: $Name \$/;
                    $require{"Name"} = 1;
                    last SWITCH;
       };
       /\$Revision:?.*\$/ && do {
                    s/\$Revision:?.*\$/\$Revision: $Revision \$/;
                    $require{"Revision"} = 1;
                    last SWITCH;
       };
       /\$Source:?.*\$/ && do {
                    s/\$Source:?.*\$/\$Source: $Source \$/;
                    $require{"Source"} = 1;
                    last SWITCH;
       };
#
#   ####Write Comments
#   ####A Tad Trickier. Comments in ClearCase can be multiple lines.
#   ####Like RCS, I assume that everything before the $Log$ is needed
#   ####for each comment line too. This is my LogLinePrefix.
#   ####Once this file is written back, I fill in the checkin comments
#   ####in the following lines.
#

       /\$Log:?.*\$/ && do {
                    $LogLineState = 1;
                    s/^(.*)\$Log:?.*\$/${1}\$Log: ${Name} \$/;
                    $LogLinePrefix = $1;
                    $require{"Log"} = 1;
       };
    }
    print WORKFILE;
    if($LogLineState) {
#
#   ####Add Checkin Comments to under Log Line
#   ####Under the $Log$ Line, I put the file version and the WIP
#   ####value if there is one (for DDTs Integration). I then
#   ####peel off one line at a time of the comments and add them to
#   ####the workfile.
#
        print WORKFILE "$LogLinePrefix $Revision $Date $Author\n";
        if ($Log) {
            $LogLine = "$Log";
            # necessary for double \r in comments from the
            # CC comment window, might be removed later
            $LogLine =~ s/\r//g;
            $LogLine =~ s/^(\\\\\[^\n\\\\\]*)/${LogLinePrefix}    ${1}/;
            $LogLine =~ s/\n(\\\\\[^\n\\\\\]*)/\n${LogLinePrefix}    ${1}/g;
            print WORKFILE "$LogLine\n";
        }
    }
}
########################################################################
# CLOSE FILES
#
close WORKFILE;
close SOURCE;

########################################################################
# CHECK TO SEE IF ALL REQUIRED FIELDS ARE FOUND
#
$requireFlag = 1;               #Assume everything is okay
$message = "";
foreach $RCSkeyword (keys(%require)) {
    if ($require{"$RCSkeyword"} == 0) {
        $requireFlag = 0;       #Missing a Required Keyword!
        $message .= "RCS Keyword <$RCSkeyword> is required but not found\n";
    }
}
if ($requireFlag == 0) {
    $message .= "\nRequired RCS Keywords are missing from file <$Name>.\n";
    $message .= "Reedit this file and insert the missing RCS Keywords\n";
    &error("$message");
}
########################################################################
# COPY FILE TO LOCATION
#
# I'd love just to use the "mv" command, but Windoze NT is a pain.
# I'll have to do the whole thing manually;
open (WORKFILE, "$workFile") ||
    &error ("Could not open Work File $workFile for reading");
open (SOURCE, ">$CC_PN") ||
    &error ("Could not open Source File $CC_PN for writing");
while(<WORKFILE>) {
    print SOURCE;
}
close(WORKFILE);
close(SOURCEFILE);
unlink($workFile);
return 0
}
########################################################################
# SUBROUTINE ERROR
#
sub error {
    close(WORKFILE);
    unlink($workFile);
    my ($message) = @_;
    die ($message."\nUnable to continue checkin ...\n");
}
#
run();
1;

When the trigger is installed, you can use it by entering the following keywords in all source files (For example, all elements of type "text_file").
$Author:$
$Date:$
$Header:$
$Id:$
$Name:$
$Source:$
$Log:$

Example (after substitution):
/* 
 ==================================================================== 
 /*           
 /*           
 /*           
 /*     This is an example of RCS keyword substitution      
 /*           
 /*          
 /* $Author: ddiebolt $        
 /*          
 /* $Date: 1999-07-16 15:16:02 GMT $       
 /*           
 /* $Header: @(#) \app_VOB\lolo.c@@\main\37  1999-07-16 15:16:02 GMT  ddiebolt $  
 /*           
 /* $Id: @(#) lolo.c@@\main\37  1999-07-16 15:16:02 GMT  ddiebolt $ 
 /*           
 /* $Name: lolo.c $         
 /*           
 /* $Revision: \main\37 $        
 /*           
 /* $Source: \app_VOB\lolo.c $       
 /*           
 /* $Log: lolo.c $         
 /*  \main\37 1999-07-16 15:16:02 GMT ddiebolt
 /*     This is a test
 /*     
 /*  \main\36 1999-07-16 15:14:14 GMT ddiebolt
 /*  \main\35 1999-07-16 15:13:17 GMT ddiebolt
 /*  \main\34 1999-07-16 15:11:58 GMT ddiebolt
 /*  \main\30 1999-07-16 14:55:00 GMT ddiebolt               
 /*           
 /*           
 /*           
 /* =========================================================== */

When you point the sticky bit to the group for a directory on UNIX (or at least on Solaris), all files created inside get the same group and directory as the primary group.
I've had requests for the same feature in ClearCase. Since ClearCase also has the sticky bit option, I expected the same output as UNIX.
Unfortunately, that's not the case. I needed to write some triggers to simulate that.
Since you can require a group assignment while creating a new directory, I decided to approach the problem a bit earlier in the process.
Here is my solution.
Trigger installation:
cleartool mktrtype -c "Sticky bit behaviour for directories" -element -all -postop mkelem -execwin "ccperl \\mw-ddiebolt\triggers\stickybit.bat" STICKYBIT
Trigger script:
@rem= 'PERL for Windows NT - ccperl must be in search path
@echo off
ccperl %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';

###########################################
# Begin of Perl section


#
# Fixed variable
#

$COMPUTERNAME           = $ENV{COMPUTERNAME};
$S                      = "\\";
$group_choosed          = "c:".$S."temp".$S."group_choosed";


#
# Configuration Variables
#

$VOB            = $ENV{CLEARCASE_VOB_PN};
$dir            = $ENV{CLEARCASE_PN};
$filename       = $ENV{CLEARCASE_PN};
$element_type   = $ENV{CLEARCASE_ELTYPE};

#printf("stickybit.bat : $VOB $dir $filename $element_type \n");

if ($element_type eq "directory")
        {
        $list_group = `cleartool describe -long VOB:$VOB | findstr group`;
        $list_group =~ s/Additional groups:\n//g;
        $list_group =~ s/group //g;
        $list_group =~ s/ //g;
        $list_group =~ s/\n/,/g;

        $status = system("clearprompt list -outfile $group_choosed -items 
$list_group -prompt \"Choose a group\" -prefer_gui");
        if ($status != 0)
                {
                exit $status;
                }
        $group  = `cmd /c type $group_choosed`;
        chop $group;
        $status = `del $group_choosed`;

#       printf("cleartool protect -chgrp $group $dir\n");
        if (!($group eq ""))
                {
                $status = system("cleartool protect -chgrp $group \"$dir\"\n");
                }
        exit $status;
        }
else
        {
        $group = `cleartool describe -fmt \"%Gu\" .`;

#       printf("cleartool protect -chgrp \"$group\" $filename\n");

        $status = system("cleartool protect -chgrp \"$group\" \"$filename\"\n");
        exit $status;
        }
fi

# End of Perl section

__END__
:endofperl

When a new directory element is created, the following dialog box appears:
Group Choice window
Figure 6: Now groups can be assigned automatically
You then must choose a group on the VOB group list, so you can protect the new directory with this group.
If you choose "Abort" or do not select any group, then the primary group will be set as the default (or your CLEARCASE_PRIMARY_GROUP setting).
When a new element is created, it will automatically be assigned a directory group.
The trigger script has the following problems:
  • It only works and is tested on Windows.
  • It is using findstr, which should be replaced by grep on UNIX.
  • Group names cannot contain any spaces.
  • Users need to be in the group chosen
With these 10 triggers, we are able to extend the flexibility and control a Software Configuration Manager should have over:
  • Handling permissions
  • Handling naming / process conventions
  • Special user requests such as automatic notification, publication, and so on

2 comments: