Tuesday, June 25, 2013

Using command-line Subversion to access project source files

Getting started with command-line Subversion

If you are participating in a development project that is using Subversion for version control, you will need to use Subversion to access and change project source files. You can browse the source code online to view a project's directory structure and files by clicking on the Subversion link in the left navigation pane for the project.
The Subversion page displays with three subdirectories: branches/, tags/, trunk/ and one README file. The README file gives a top level view of the Subversion repository. You can click Access options to view the Subversion client setup instructions. You must have a Subversion client installed on your local machine.
Getting a local working copy for your projectsvn checkout
To get a "working copy" of the latest source files, you must check out the source files, a process which copies the files onto your system from the repository. In your shell or terminal client, type:
svn checkout https://(projectname).(domain)/svn/(projectname)/(DIR) (projectname) --username [type-user-name-here]
Enter your user password when prompted. This should be the same password associated with your user account on this site. Not specifying the directory will checkout the entire project source code. You may want to checkout the 'trunk/' directory as it has the working 'www/' folder.
Switches: --revision (-r) REV, --quiet (-q), --non-recursive (-N), --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Note: If your site is not SSL (Secured Socket Layer) enabled, use http instead of https to perform a Subversion operation. For example, to checkout the latest source code from a project's repository, use this command:
svn checkout https://(projectname).(domain)/svn/(projectname)/(DIR) (projectname) --username [type-user-name-here]

Working with files in the Subversion repository

Once you have checked out a local copy of the project repository, you can edit the existing files, create new files and directories, and delete files locally. Any changes you make affect only your local copies of the project files until you commit your changes back to the Subversion repository.
Adding files/directories from your working copy to the repositorysvn add
You can add a new file to the repository after you have first created and edited it in your working directory or add a directory with or without it's contents using
svn add FILENAME/DIR.
This will add files/directories to your working copy and schedule them for addition to the repository. They will be uploaded and added to the repository on your next commit. If you add something and change your mind before committing, you can unschedule the addition using svn revert.
Switches: --targets FILENAME, --non-recursive (-N), --quiet (-q), --config-dir arg, --auto-props, --no-auto-props
Viewing the content of specific files with revision and author informationsvn blame
You can view the author and revision information in-line for the specified files by typing:
svn blame FILENAME
Each line of text is annotated at the beginning with the author (username) and the revision number for the last change to that line.
Switches: --revision (-r) REV, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Viewing the content of specific filessvn cat
At times, your working copy may be obsolete as compared to the repository or you may have modified your local working copy. In such cases, you will want to see the differences in the content of a specific file before you commit your changes or decide edit your working copy. Running svn cat FILENAME will automatically fetch the HEAD revision from the repository. This subcommand is mainly used to retrieve a file as it existed in an older revision number and display it on your screen. Alternatively, you can browse the source code of your project on this site to do the same.
Switches: --revision (-r) REV, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Cleaning up the working copysvn cleanup
Sometimes, you may get a "working copy locked" error. To remove the locks and recursively clean up the working copy, use svn update.
Switches: --diff3-cmd CMD, --config-dir DIR
Copying file or directory in a working copy or in the repositorysvn copy
Your project may require you to make use of legacy documents. For example, you may want to use an already existing HTML file and use its code as reference to maintain the look and feel of the product while creating fresh content. Instead of creating a file from scratch, you can simply copy this file using svn copy, save it under a different name and change the content. You can copy a file from the repository to your local working copy or vice versa. You can also copy files from within your local working copy. Subversion does not support cross repository copying. Use svn copy SRC DST to achieve this.
Switches: --message (-m) TEXT, --file (-F) FILE, --revision (-r) REV, --quiet (-q), --username USER, --password PASS, --no-auth-cache, --non-interactive, --force-log, --editor-cmd EDITOR, --encoding ENC, --config-dir DIR
See svn copy
Deleting a file or a directory from your local working copysvn delete
You may want to delete unwanted files from your local working copy. Using svn delete FILENAME schedules it to be deleted. The actual deletion of the file in the repository takes place only when you commit.
Switches: --force, --force-log, --message (-m) TEXT, --file (-F) FILE, --quiet (-q), --targets FILENAME, --username USER, --password PASS, --no-auth-cache, --non-interactive, --editor-cmd EDITOR, --encoding ENC, --config-dir DIR
Viewing the differences between filessvn diff
You can use svn diff to display local modifications in a specified file in your working copy against the one in the repository. In the command prompt, type:
svn diff (PATH of the file) (URL of the project's repository)
For example, to compare a locally modified file "index.html" against the one in the project's repository, type:
svn diff $SRC/...../index.html https://(projectname).(domain)/svn/(projectname)/trunk (projectname) --username [type-user-name-here]
Alternatively, you can go to the directory where the file belongs and type:
svn diff (FILENAME)
This will display the difference with the revision number.
Switches: --revision (-r) REV, --old OLD-TARGET, --new NEW-TARGET, --extensions (-x) "ARGS", --non-recursive (-N), --diff-cmd CMD, --notice-ancestry, --username USER, --password PASS, --no-auth-cache, --non-interactive, --no-diff-deleted, --config-dir DIR
Exporting a clean directory tree on your local machinesvn export
You can extract an unversioned copy, that is, a clean directory of a tree, on your local machine from the project repository or from within your local working copy. To get a clean directory of an older revision from the repository, type:
svn export [-r REV] [PATH]
This will export a clean directory tree from the repository specified by URL, at a revision REV (if specified), otherwise at HEAD, into PATH. If PATH is omitted, the last component of the URL is used for the local directory name. Alternatively, you can also export a clean directory tree from the working copy specified by PATH1 into PATH2 within your local machine. This will preserve all local changes, but will not copy files under version control. To achieve this, type:
svn export PATH1 PATH2
Switches: --revision (-r) REV, --quiet (-q), --force, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Getting help on subversionsvn help
Subversion offers you help within the command-line interface. To get help on a specific subcommand, type:
svn help [SUBCOMMAND...]
Switches: --version, --quiet (-q)

Contributing your changes to the SVN repository

After making changes to files and/or directories locally, you must commit those changes to the SVN repository.
Committing your changessvn commit
To commit your changes into the shared repository, type:
svn commit -m "Type your justification here"
If you do not include a description of your change to the file, you will be prompted to add it by invoking your file editor before svn can complete the commit action or you will get a "Commit failed" error. All commits are logged automatically and posted to the project's commits discussion.
Switches: --message (-m) TEXT, --file (-F) FILE, --quiet (-q), --non-recursive (-N), --targets FILENAME, --force-log, --username USER, --password PASS, --no-auth-cache, --non-interactive, --encoding ENC, --config-dir DIR
Importing an unversioned file or tree in the project repositorysvn import
You can recursively commit an unversioned file or tree into the project repository using svn import. Parent directories are created in the repository as required. The following command will recursively commit a copy from the PATH to the URL. If PATH is omitted "." is assumed.
svn import [PATH] URL
Example: To create an unversioned directory (D) with a file on your local machine. Navigate to your Subversion page by clicking the Subversion link in the left navigation pane for your project. Note that NEWDIR is not listed under Browse source code. To import D into your project's repository, type:
svn import -m "Type your message here" D http://(projectname).(domain)/svn/(projectname)/NEWDIR
Refresh the page. Note that D is listed under Browse source code. Click D to see the file.
Switches: --message (-m) TEXT, --file (-F) FILE, --quiet (-q), --non-recursive (-N), --username USER, --password PASS, --no-auth-cache, --non-interactive, --force-log, --editor-cmd EDITOR, --encoding ENC, --config-dir DIR, --auto-props, --no-auto-props
Printing information about paths in your working copysvn info
You will from time to time need specific information about files in your working copy to execute certain subcommands. Typing svn info will print exhaustive but useful information about items in your working copy paths in your working copy, including: Path, Name, URL, Revision, Node Kind, Last Changed, author, Last Changed Revision, Last Changed Date, Text Last Updated, Properties Last Updated and Checksum.
Switches: --targets FILENAME, --recursive (-R), --config-dir DIR
Viewing list of directory entries in the repositorysvn list
Before starting work on a project or fetching a 'working copy', you may want to see the contents i.e. directories and files in your project's repository or view directory entries in your local working copy. You can type svn list [TARGET...] in the command prompt to view the same. Alternatively, you can view your project's repository by navigating to the Software configuration management page within your project.
Switches: --revision (-r) REV, --verbose (-v), --recursive (-R), --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Viewing commit log messagessvn log
You can view the individual file/directory histories of the files/directories in your 'working copy' or the repository to track revision information by typing:
svn log [PATH]
The result is a display of the file's/directories' revision information, starting with the most current revision with information such as the commit messages and the author name. Alternatively, you can use this site to view the commit log messages for individual files in your project repository. Click the Subversion link from the left navigation pane of your project. The Subversion page appears. Search for your file inside the directories under Browse source code and click the filename. This will display a page with commit log messages.
Switches: --revision (-r) REV, --quiet (-q), --verbose (-v), --targets FILENAME, --stop-on-copy, --incremental, --xml, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
See svn log
Merging changessvn merge
You can run the svn merge command to tell Subversion to merge the latest versions of files from the repository into your working copies.
Switches: --revision (-r) REV, --non-recursive (-N), --quiet (-q), --force, --dry-run, --diff3-cmd CMD, --ignore-ancestry, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR

Working with the repository

Creating new directorysvn mkdir
To create a new directory in your working copy, type:
svn mkdir PATH
To create a new directory in your project repository, type:
svn mkdir URL
The final component of the PATH or URL determines the directory name. A directory in the repository is created with an immediate commit, so it requires a commit message
Switches: --message (-m) TEXT, --file (-F) FILE, --quiet (-q), --username USER, --password PASS, --no-auth-cache, --non-interactive, --editor-cmd EDITOR, --encoding ENC, --force-log, --config-dir DIR
Moving a file or a directorysvn move
You can move a file or a directory within your working copy or within your project's repository using svn move SRC DST. This command is equivalent to an svn copy followed by svn delete. Moving a file or a directory within your working copy moves and schedules it for addition for the next commit. Moving a file or a directory within your project repository is an atomic commit, so it requires a commit message.
Switches: --message (-m) TEXT, --file (-F) FILE, --revision (-r) REV, --quiet (-q), --force, --username USER, --password PASS, --no-auth-cache, --non-interactive, --editor-cmd EDITOR, --encoding ENC, --force-log, --config-dir DIR
Working with properties
Subversion has a number of specific properties that affect or determine it's behavior. You can modify, commit, and revert property changes just like the contents of your files. You can delete, edit, print, list, set a property from files, directories or revisions from your local working copy or your project's repository.
Resolving conflictssvn resolved
You may get a conflict while updating your local working copy. You will need to resolve the conflict. After resolving, type svn resolved PATH.... to tell your working copy that the conflict has been "resolved."
Switches: --targets FILENAME, --recursive (-R), --quiet (-q), --config-dir DIR
Reverting your changessvn revert
As you work with Subversion, you will realize that svn revert PATH... is equivalent to a Ctrl Z on Windows. You can:
  • Revert any local changes on your local working copy and thus, resolve any conflicted states.
  • Revert the contents of an item and the property changes in your working copy.
  • Cancel out any scheduling operations like file addition, file deletion etc.
NOTE that not providing any target may result in loss of changes in your working copy.
Switches: --targets FILENAME, --recursive (-R), --quiet (-q), --config-dir DIR
Getting status of files/directoriessvn status
It is a good practice in version control to review your changes before committing them to the project's repository. You can run svn status to print the status of the files and directories in your working copy. This will result in a coded eight column output. It is humanly impossible to draw an 'error-free' conclusion from the output as each column has an exhaustive legend. To make this task simpler and simultaneously see an example, type svn help status in your command prompt.
Switches: --show-updates (-u), --verbose (-v), --non-recursive (-N), --quiet (-q), --no-ignore, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir
Switching your working copysvn switch
You can update your working copy to mirror a new URL using svn switch URL [PATH]. You can move a working copy or a part of your working copy to a new branch. You can use this subcommand as a shortcut for branching.
Switches: --revision (-r) REV, --non-recursive (-N), --quiet (-q), --diff3-cmd CMD, --relocate, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Updating your working copysvn update
As a good version control practice, it is recommended that you update your local working copy with the project repository everyday using:
svn update [PATH...]
The updated items listed with their current status indicated as follows:
  • A = A file was added to your working copy.
  • U = A file was updated to your working copy.
  • D = A file was deleted from your working copy.
  • R = A file was replaced in your working copy.
  • G = A file was successfully merged.
  • C = A file has merge conflicts that must be resolved by hand
Switches: --revision (-r) REV, --non-recursive (-N), --quiet (-q), --diff3-cmd CMD, --username USER, --password PASS, --no-auth-cache, --non-interactive, --config-dir DIR
Branching and tagging
A project's trunk is usually used for the main line of development whereas branches are usually used for variations on that line. A branch is an ongoing line of development. In a Software Development Life Cycle, branches are often used when a public release of a software product is due, to allow testers to work on the release candidate so that new development can go on independently of the testing. Branches are also used for experimental work and a complete code rewrite. Tagging is a way of marking a group of file revisions as belonging together. Though branches and tags are created using the svn copy subcommand, branches and tags are different things. A branch represents multiple revisions while a tag represents a single revision.
The Subversion repository for your project hosted on this site supports branching and tagging your source files. Tagging and branching are easy and practical 'copy' operations for Subversion.
To create a branch or tag project files, type:
svn copy SRC DST -m "Type your message here"

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