Perforce Public Knowledge Base - Enforcing Workspace Configurations
Perforce Software logo
Reset Search
 

 

Article

Enforcing Workspace Configurations

« Go Back

Information

 
Problem

How can I make sure that my users are creating and using workspaces with specific options?

Solution

Use a form-in trigger to enforce specific options when clients are created or modified.

Use a change-submit trigger to check for commits made with older (possibly non-valid) workspaces.

Overview

A trigger is composed of two parts: A trigger entry in the Perforce server, and an associated script or program that resides externally on the machine where the Perforce server is being hosted.

The solution consists of two separate triggers and scripts:

  1. The first trigger and script checks new or modified client workspaces (or and confirms that the proper SubmitOptions value are set. If it is not set to a valid option, the script sets it to a valid option.

    Note: Alternately the script could reject the client workspace with a message to correct the options. In most cases re-setting the options as the workspace is submitted is appropriate.
     
  2. The second trigger and script checks changes as submitted to the Perforce database. If a submit is not performed with a correctly configured workspace, then the triggers reject the commit with an appropriate error message.

    Note: This trigger is intended to catch obsolete workspaces created before the workspace checking trigger was implemented. You could also, as a super user, run through all of the workspaces manually or with a script to reset those options.

Checking new or edited workspaces for the proper configuration

Let us start with the trigger to determine whether a new workspace has the proper SubmitOptions. If the value is not valid, we set it to "revertunchanged".

This Python script will receive a single variable, %formfile%, which is the path to the temporary file holding the submitted client specification:

# wschk.py
#
# A Python script to check workspaces for a valid SubmitOptions
# value.  If a valid option isn't specified, we set the 
# SubmitOptions field to the specified default.
#
# Takes a single argument - the path to the temporary form.  
# Perforce triggers expect nothing back on success, so any
# non-zero return value will cause the trigger to reject
# the client submission and report a failure.

# Set program parameters here
DEFAULT = "revertunchanged"
VALID = [
	"revertunchanged",
	"revertunchanged+reopen",
	"leaveunchanged",
	"leaveunchanged+reopen",
]

import os, sys
import re

SO_RE = re.compile("^SubmitOptions:")

def check_spec(client_file):
	"""
	This function checks the client specification contained
	in client_file for a correct SubmitOptions value and 
	sets it to the default if an incorrect value is set.
	"""
	fd = open( client_file, "r")
	client_spec = fd.readlines()
	fd.close()

	# Find the SubmitOptions line	
	cur = 0
	for line in client_spec:
		if re.search("^SubmitOptions:", line):
			# Check if the value is valid:
			so_value = line.split()[1]
			
			if so_value in VALID:
				# Valid option, return
				sys.exit(0)
			
			else:
				# Set the default and write back to the file
				# then return
				client_spec[cur] = "SubmitOptions:\t%s\n" % DEFAULT
				fd = open(client_file,"w")
				fd.writelines(client_spec)
				fd.close()
				sys.exit(0)

		cur += 1

	# We shouldn't get this far.  If we do, it means the file 
	# doesn't have a SubmitOptions line, which means it's
	# not a client spec.  Report an error, then quit.
	print("Warning: %s does not contain a client spec, or the " +
		"client spec is not properly formatted.")
	print("Error: 'SubmitOptions' field not found!")
	sys.exit(1)
	

if __name__ == "__main__":

	# Make sure we have the right amount of arguments
	if(len(sys.argv) !=2):
		print("Invalid number of arguments, expected 1, got %d" % len(sys.argv))
		sys.exit(1)

	# Check to make sure the temp form file exists
	if not os.path.isfile(sys.argv[1]):
		print("Could not access the temporary form file %s" % argv[1])
		sys.exit(1)

	# Verify the client spec and return 
	check_spec(sys.argv[1])

In order to use this script, set up a trigger to inform the server that submitted client specs need to be passed to a script for validation. Enter the command:

p4 triggers

This opens the trigger specification in your default editor. Add the following line:

wschk form-in client "python {path to}wschk.py %formfile%"

Substitute the proper path for {path to}wschk.py.

Note: If the trigger is stored in the Perforce server root directory, then the path is not required. In the event of storing trigger scripts in a server sub-directory, you can use relative path syntax. For example, if the script is stored in a "triggers" directory in the Perforce server root directory:

wschk form-in client "python triggers/wschk.py %formfile%"

To test the new triggers, try creating a new workspace:

p4 client trigger_test

Edit the specification to use one of the unapproved SubmitOptions, for example, 'submitunchanged'.

Note: Since "submitunchanged" is the default option, not changing this option will always result in a rejected workspace specification.

SubmitOptions: submitunchanged

Save and exit the workspace specification.

Client trigger_test saved.

To check the workspace, enter the command:

p4 client -o trigger_test

Note that the specification now has this line:

SubmitOptions: revertunchanged

Preventing Submits from workspaces with the incorrect options set

Similar to the trigger checking new and edited workspaces, a trigger and an external script is used to check the validity of the workspace used during a commit. The trigger receives a single variable, %client%, which contains the name of the workspace used. Unlike the previous script, this script makes a call to Perforce using the p4 command line client, so the command line client must be installed on the Perforce server host.

Note: Since this script makes calls the p4 command line client, you will need to confirm that the script has the appropriate access to the Perforce server.  If you require a password and can use it on the command line, then modify the script to pass a "-P <password>" to p4.  For ticket-based authentication, see the following link on how you can get triggers set up to use ticket-based authentication.

Note the use of the "-G" flag to p4.  This requests that p4 command return the output of the command in the form of a Python marshaled object, which is a convenient for the script to read through the output without having to resort to parsing.

# submitchk.py
#
# A python script that takes a single variable representing the name
# of a Perforce client specification and performs a check of that
# specification to verify that particular options are being used.
#
# This script checks the 'SubmitOptions' field for valid options, and returns
# 0 is it's valid, or an error message if it's not. Perforce triggers 
# expect nothing back on success, so any non-zero return value will 
# cause the trigger to reject the client submission and report a failure.
#
# Set the list of valid options here

VALID = [
	"revertunchanged",
	"revertunchanged+reopen",
	"leaveunchanged",
	"leaveunchanged+reopen"
]

# Path to the p4 command line client, and required options

P4 		= 'C:\\"Program Files"\\Perforce\\p4.exe'
P4PORT 	= "localhost:1666"
P4USER  = "wyvern"

import sys, os
import marshal

def check_ws(workspace_name):
	# Check the given workspace name for the valid options


	# First we grab the value of the SubmitOptions field.  We 
	# use marshalling in combination with the '-G' option to
	# get a Python dict.
	CMD = "%s -u %s -p %s -G client -o %s" % ( P4, P4USER, P4PORT, workspace_name )
	pd = os.popen( CMD )
	client_spec = marshal.load(pd)
	
	# Check SubmitOptions
	if client_spec.has_key('SubmitOptions'):
		if client_spec['SubmitOptions'] in VALID:
			# Valid option, return 	
			sys.exit(0)
		else: 
			# Invalid option, report error message and return
			print("Invalid workspace option: %s" % client_spec['SubmitOptions'])	
			sys.exit(1)
	else:
		# Can't grab the workspace spec, report error and exit
		print("System error: Unable to obtain workspace %s" % workspace_name)
		sys.exit(1)


if __name__ == "__main__":
	# Main entry point.  Check that we have an argument, then
	# call check_ws

	if(len(sys.argv) !=2):
		print("Invalid number of arguments, expected 1, got %d" % (len(sys.argv)-1))
		sys.exit(1)

	check_ws(sys.argv[1])

Add the appropriate trigger table entry by entering the command:

p4 triggers

This opens the trigger specification in your default editor. Add the following line:

submitchk change-submit //... "python {path to}submitchk.py %client%"

As before, substitute the {path to} actual path to the trigger script.

To test, you will need to create a client prior to adding the trigger table entry. Keep the default SubmitOptions. After adding the trigger table entry, check out a file for edit and attempt to submit. For example:

C:\users\wyvern\Documents\local>p4 edit foo
//depot/foo#1 - opened for edit

C:\users\wyvern\Documents\local>p4 submit -d test
Submitting change 10.
Locking 1 files ...
Submit validation failed -- fix problems then use 'p4 submit -c 10'.
'submitchk' validation failed: Invalid workspace option: submitunchanged

C:\users\wyvern\Documents\local>

The trigger prevented the submission of the change from an workspace with invalid SubmitOptions. To submit, change the workspace to "revertunchanged+reopen" and attempt to submit the change:

C:\users\wyvern\Documents\local>p4 submit -c 10
Submitting change 10.
edit //depot/foo#2
//depot/foo#2 - unchanged, reverted
No files to submit.

This time the trigger did not fire, because the workspace "SubmitOptions" value were valid.

Note: It is strongly recommended that all testing of triggers be done on a duplicate of your Perforce Helix server. Use this online form to obtain a duplicate server license. Contact Perforce Support if you have any questions.

Related Links
https://www.perforce.com/support-services/duplicate-server-request

Feedback

 

Was this article helpful?


   

Feedback

Please tell us how we can make this article more useful.

Characters Remaining: 255