February 13, 2013

Put the brakes on! SP_NOCHECKIN

Surround SCM
StopFrom time to time we all find it necessary to write code we have no intention of checking in. Maybe you hacked in a feature where it didn't belong so you could test it early or added some debug code that has no business going into the product. Wouldn't it be nice if our tools enforced removing these changes before checking in to the production code base? Turns out Surround SCM can do just that. Surround’s executable trigger feature provides a way to plug-in custom functionality. Crucially, Surround is also happy to put the brakes on a check in. This small Python script looks at the file we’re checking in to see if it contains a token we use to mark code that should never escape our development environment. In this case, that token is “SP_NOCHECKIN”. We agree on a practice: for code we don’t intend to check in, we drop this token in a comment right next to it at the time we write it. If we forget to remove the token and related code before check in, we’re protected.
import os
import re
# The token to look for
rejectToken = 'SP_NOCHECKIN'
# Constants for readability
exitval_Reject = 1
exitval_Accept = 0
# Default exit value
exitValue = exitval_Accept
# Beginning of default reject message
exitMessage = 'This file is rejected because '
try:
	tempFileName = os.environ['SSCM_LOCALFILE']
	try:
		# Open file and look for rejectToken
		file = open(tempFileName, 'r')
		for line in file:
			if re.search(rejectToken, line):
				exitValue = exitval_Reject
				exitMessage += 'the file text contains '
				exitMessage += rejectToken
				break
		file.close()
	except:
		exitMessage += 'the temporary file could not be opened'
		exitMessage += ' to scan for ' + rejectToken
		exitValue = exitval_Reject
except:
	# our environment var isn't defined.  Could be doing something 
	# like a roll back.  Accept by default.
	exitValue = exitval_Accept
if exitValue != exitval_Accept:
	print exitMessage
exit(exitValue)
The Surround SCM server calls the script when the trigger executes, and communicates with it through environment variables. The script can communicate back to Surround through a return value and console output. A zero return value indicates Surround should accept the check in, while a non-zero indicates it should prevent the check in. On prevent, any text printed to the console is displayed as a message to the user. We could just as easily be looking over the new file contents for coding standards violations or running and interpreting the results of a static source analysis tool. Now that we have our script, we create a pre-event trigger that fires anytime a user checks in a file. Starting at Tools->Administration->Triggers->Add… Since we’re making assumptions about the content of the files we’re scanning— that it is text and not binary—we only want to run our trigger on certain file types. We use a filename precondition to exclude other types of files. Regular expressions allow us to apply a single trigger to an arbitrary number of file extensions. Another precondition we want is to make sure only commits by certain users are checked. You might have automated tools or other services where code commits should never be rejected. After preconditions, the next pane determines when the trigger activates. Since we want an opportunity to intervene before file changes are committed we want this trigger to occur just before the file version is updated. And finally, the trigger should run the Python script. And the result—bad code polluting the code base—has been averted.